<?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: Ahmed Rehan</title>
    <description>The latest articles on DEV Community by Ahmed Rehan (@ar27111994).</description>
    <link>https://dev.to/ar27111994</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%2F3678450%2F7fe013eb-7f8e-407a-8400-ac85724d8a4f.jpeg</url>
      <title>DEV Community: Ahmed Rehan</title>
      <link>https://dev.to/ar27111994</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ar27111994"/>
    <language>en</language>
    <item>
      <title>Agent assets need a lifecycle, not a dumping ground</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Mon, 18 May 2026 09:08:08 +0000</pubDate>
      <link>https://dev.to/ar27111994/agent-assets-need-a-lifecycle-not-a-dumping-ground-1i3h</link>
      <guid>https://dev.to/ar27111994/agent-assets-need-a-lifecycle-not-a-dumping-ground-1i3h</guid>
      <description>&lt;p&gt;I recently shipped &lt;a href="https://github.com/ar27111994/agent-harness" rel="noopener noreferrer"&gt;&lt;code&gt;agent-harness&lt;/code&gt;&lt;/a&gt; v1.0.0.&lt;/p&gt;

&lt;p&gt;It is a Node.js / TypeScript CLI for discovering, staging, activating, and wiring reusable AI-agent assets across VS Code / GitHub Copilot, Cursor, OpenCode, Zed, Claude Code, and Pi.&lt;/p&gt;

&lt;p&gt;The first announcement explained what it is.&lt;/p&gt;

&lt;p&gt;This follow-up is about the design problem behind it — and why I am looking for more than page views or stars.&lt;/p&gt;

&lt;p&gt;I want criticism, feature suggestions, issue reports, tickets, and code contributions from people who actually use AI coding tools in real projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is not “more context”
&lt;/h2&gt;

&lt;p&gt;A lot of AI coding workflows drift toward the same pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collect more prompts&lt;/li&gt;
&lt;li&gt;collect more skills&lt;/li&gt;
&lt;li&gt;collect more examples&lt;/li&gt;
&lt;li&gt;collect more MCP configs&lt;/li&gt;
&lt;li&gt;collect more instructions&lt;/li&gt;
&lt;li&gt;put them somewhere the model can see&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That can be useful, but it has a cost.&lt;/p&gt;

&lt;p&gt;Context is not free. Even when the tokens are technically available, attention is still scarce. Irrelevant instructions compete with useful ones. Tool-specific assumptions leak into repos where they do not belong. Old experiments stick around because removing them is less obvious than adding another file.&lt;/p&gt;

&lt;p&gt;At some point the workflow stops feeling like “better assistance” and starts feeling like a context landfill.&lt;/p&gt;

&lt;p&gt;The question I wanted to explore was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you make useful agent assets portable across projects and tools without dragging everything into every workspace?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That question became &lt;code&gt;agent-harness&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a lifecycle?
&lt;/h2&gt;

&lt;p&gt;The core idea in &lt;code&gt;agent-harness&lt;/code&gt; is that reusable agent assets should move through a lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;discover&lt;/li&gt;
&lt;li&gt;mirror / stage&lt;/li&gt;
&lt;li&gt;install&lt;/li&gt;
&lt;li&gt;activate&lt;/li&gt;
&lt;li&gt;wire&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That may sound like extra ceremony. In some cases, it probably is.&lt;/p&gt;

&lt;p&gt;But the lifecycle exists because “available somewhere” and “active in this repo right now” are very different states.&lt;/p&gt;

&lt;p&gt;A skill, instruction, prompt pack, MCP server, or workflow might be useful in general but wrong for a specific repo. It might be relevant to a frontend project but noise in a backend service. It might be useful for Cursor but not for Claude Code. It might be safe to stage but not safe to activate automatically.&lt;/p&gt;

&lt;p&gt;A lifecycle gives the system places to make those distinctions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery should not mean activation
&lt;/h2&gt;

&lt;p&gt;One of the mistakes I wanted to avoid was treating discovery as permission to inject context.&lt;/p&gt;

&lt;p&gt;Discovery should answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What might be relevant?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Activation should answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What should actually be active here?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are not the same question.&lt;/p&gt;

&lt;p&gt;If a project looks like a Flutter app, it may be reasonable to discover mobile, Android, Firebase, testing, and UI-related assets. But that does not mean every vaguely mobile-related asset should be wired into the editor context.&lt;/p&gt;

&lt;p&gt;The tighter the activation boundary, the less likely the system is to bloat the workspace with clever-but-irrelevant material.&lt;/p&gt;

&lt;h2&gt;
  
  
  Host-aware wiring matters
&lt;/h2&gt;

&lt;p&gt;Different AI coding hosts expose different surfaces.&lt;/p&gt;

&lt;p&gt;VS Code / GitHub Copilot, Cursor, OpenCode, Zed, Claude Code, and Pi do not all consume project instructions, prompts, tools, and local assets in the same way.&lt;/p&gt;

&lt;p&gt;Pretending they are the same usually produces either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a lowest-common-denominator setup that wastes host-specific strengths, or&lt;/li&gt;
&lt;li&gt;a messy pile of adapters hidden behind vague “it works everywhere” claims&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;agent-harness&lt;/code&gt; leans into host differences instead. It tries to keep assets reusable while still wiring them through the surfaces each host actually supports.&lt;/p&gt;

&lt;p&gt;That is the bet, at least.&lt;/p&gt;

&lt;p&gt;It may turn out that the abstraction is too complex. That is exactly the kind of feedback I want.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable part: this started vibe-coded
&lt;/h2&gt;

&lt;p&gt;This project was mostly vibe-coded at the start.&lt;/p&gt;

&lt;p&gt;I think that matters to say plainly.&lt;/p&gt;

&lt;p&gt;The good part is that it came from real workflow pain and moved quickly. The bad part is that vibe-coded systems can easily grow accidental abstractions that make sense only to the person building them.&lt;/p&gt;

&lt;p&gt;So after the initial build, I pushed it through a stricter phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;release audits&lt;/li&gt;
&lt;li&gt;real workspace testing&lt;/li&gt;
&lt;li&gt;Windows validation&lt;/li&gt;
&lt;li&gt;issue-driven cleanup&lt;/li&gt;
&lt;li&gt;host adapter tightening&lt;/li&gt;
&lt;li&gt;docs and changelog cleanup&lt;/li&gt;
&lt;li&gt;release workflow hardening&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is more disciplined than the prototype, but I still do not want to assume the idea is automatically good.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I want from other developers
&lt;/h2&gt;

&lt;p&gt;I am not looking for generic praise on this one.&lt;/p&gt;

&lt;p&gt;I am looking for validation or invalidation.&lt;/p&gt;

&lt;p&gt;Useful feedback would be things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“this should be much smaller”&lt;/li&gt;
&lt;li&gt;“this host should not be supported”&lt;/li&gt;
&lt;li&gt;“this lifecycle is unnecessary ceremony”&lt;/li&gt;
&lt;li&gt;“this should just be config files”&lt;/li&gt;
&lt;li&gt;“this breaks in my repo because…”&lt;/li&gt;
&lt;li&gt;“the restrained alternative framing does / does not hold up”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would also really value concrete contribution paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;feature suggestions for missing lifecycle steps or host surfaces&lt;/li&gt;
&lt;li&gt;issues/tickets for rough edges, unclear docs, or broken assumptions&lt;/li&gt;
&lt;li&gt;PRs that improve host adapters, discovery sources, tests, docs, or examples&lt;/li&gt;
&lt;li&gt;small reproducible fixtures from real projects where the selection logic gets it wrong&lt;/li&gt;
&lt;li&gt;examples of workflows this should support but currently does not&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, the most useful thing you can do is open an issue or discussion with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repo type / stack&lt;/li&gt;
&lt;li&gt;host used&lt;/li&gt;
&lt;li&gt;OS&lt;/li&gt;
&lt;li&gt;what you expected&lt;/li&gt;
&lt;li&gt;what actually happened&lt;/li&gt;
&lt;li&gt;what felt confusing or unnecessary&lt;/li&gt;
&lt;li&gt;whether the core idea seems useful or wrong&lt;/li&gt;
&lt;li&gt;what feature or simplification you would want next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/ar27111994/agent-harness" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release: &lt;a href="https://github.com/ar27111994/agent-harness/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness/releases/tag/v1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@ar27111994/agent-harness" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@ar27111994/agent-harness&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Feedback discussion: &lt;a href="https://github.com/ar27111994/agent-harness/discussions/218" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness/discussions/218&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the idea is wrong, I would rather learn that early.&lt;/p&gt;

&lt;p&gt;If the idea is useful but incomplete, I would love help shaping it through issues, feature requests, and PRs.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>typescript</category>
      <category>devtools</category>
    </item>
    <item>
      <title>I built a more restrained alternative to giant AI skill bundles</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Fri, 08 May 2026 13:42:35 +0000</pubDate>
      <link>https://dev.to/ar27111994/i-built-a-more-restrained-alternative-to-giant-ai-skill-bundles-1kf5</link>
      <guid>https://dev.to/ar27111994/i-built-a-more-restrained-alternative-to-giant-ai-skill-bundles-1kf5</guid>
      <description>&lt;p&gt;I just shipped &lt;a href="https://github.com/ar27111994/agent-harness" rel="noopener noreferrer"&gt;&lt;code&gt;agent-harness&lt;/code&gt;&lt;/a&gt; v1.0.0.&lt;/p&gt;

&lt;p&gt;It’s a Node.js / TypeScript CLI for discovering, staging, activating, and wiring reusable AI-agent assets across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VS Code / GitHub Copilot&lt;/li&gt;
&lt;li&gt;Cursor&lt;/li&gt;
&lt;li&gt;OpenCode&lt;/li&gt;
&lt;li&gt;Zed&lt;/li&gt;
&lt;li&gt;Claude Code&lt;/li&gt;
&lt;li&gt;Pi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/ar27111994/agent-harness" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release: &lt;a href="https://github.com/ar27111994/agent-harness/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness/releases/tag/v1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@ar27111994/agent-harness" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@ar27111994/agent-harness&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Feedback discussion: &lt;a href="https://github.com/ar27111994/agent-harness/discussions/126" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness/discussions/126&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I made this
&lt;/h2&gt;

&lt;p&gt;This project came from a very specific frustration.&lt;/p&gt;

&lt;p&gt;There are already a lot of big all-in-one AI skills/prompts/context bundles out there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;antigravity-awesome-skills&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cursor-skills&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;awesome-agent-skills&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;awesome-claude-skills&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;awesome-copilot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;and similar projects in that orbit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of those are useful. I’m not against them.&lt;/p&gt;

&lt;p&gt;But for my own day-to-day project mix, they often felt too blunt.&lt;/p&gt;

&lt;p&gt;The problem wasn’t “I need more context.”&lt;/p&gt;

&lt;p&gt;It was more like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;too much irrelevant context getting pulled in&lt;/li&gt;
&lt;li&gt;too little selectivity&lt;/li&gt;
&lt;li&gt;too much bundle gravity&lt;/li&gt;
&lt;li&gt;not enough control over what gets wired where&lt;/li&gt;
&lt;li&gt;too much risk of bloating already-expensive context windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I wanted something narrower and more restrained.&lt;/p&gt;

&lt;p&gt;Not a mega-bundle.&lt;br&gt;
Not a giant dump of everything.&lt;br&gt;
Not “install this, and now your AI stack has 500 things attached to it.”&lt;/p&gt;

&lt;p&gt;I wanted something that tries to answer a more practical question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you make reusable agent assets portable across tools and projects without turning every repo into a context landfill?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s the reason &lt;code&gt;agent-harness&lt;/code&gt; exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually does
&lt;/h2&gt;

&lt;p&gt;At a high level, &lt;code&gt;agent-harness&lt;/code&gt; manages a lifecycle for reusable agent assets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;discover&lt;/strong&gt; what might be relevant to a workspace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mirror&lt;/strong&gt; and stage reproducible artifacts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;install&lt;/strong&gt; them into lifecycle-aware host stores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;activate&lt;/strong&gt; selected assets into runtime views&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;wire&lt;/strong&gt; them into the target host/workspace&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important part for me is not just that it does these steps.&lt;/p&gt;

&lt;p&gt;It’s that it tries to do them &lt;strong&gt;selectively&lt;/strong&gt; and &lt;strong&gt;host-aware&lt;/strong&gt;, instead of treating every tool as one undifferentiated pile of prompts and skills.&lt;/p&gt;

&lt;p&gt;That distinction mattered a lot in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I was optimizing for
&lt;/h2&gt;

&lt;p&gt;I was not trying to build the biggest possible agent asset bundle.&lt;/p&gt;

&lt;p&gt;I was optimizing for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;portability across different hosts&lt;/li&gt;
&lt;li&gt;reuse across different kinds of repos&lt;/li&gt;
&lt;li&gt;more disciplined context injection&lt;/li&gt;
&lt;li&gt;less irrelevant baggage&lt;/li&gt;
&lt;li&gt;a workflow that survives real product development instead of just looking impressive in a screenshot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s also why the project ended up focusing on discovery, staging, activation, and wiring instead of just curating one huge collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest note: this was mostly vibe-coded
&lt;/h2&gt;

&lt;p&gt;This project was mostly vibe-coded at the start.&lt;/p&gt;

&lt;p&gt;That probably shows in both good and bad ways.&lt;/p&gt;

&lt;p&gt;The good side is that it moved quickly and came from a very real pain point.&lt;br&gt;
The bad side is that projects built like this can drift into strange abstractions, workflow-specific assumptions, and over-engineering.&lt;/p&gt;

&lt;p&gt;So after getting the core idea working, I pushed it through a much less romantic phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;release audits&lt;/li&gt;
&lt;li&gt;real workspace testing&lt;/li&gt;
&lt;li&gt;Windows-specific validation&lt;/li&gt;
&lt;li&gt;issue-driven cleanup&lt;/li&gt;
&lt;li&gt;host adapter tightening&lt;/li&gt;
&lt;li&gt;docs/changelog/release workflow cleanup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: it started loose, then got forced into a stricter shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I want now is criticism
&lt;/h2&gt;

&lt;p&gt;At this point, I’m not really looking for generic applause.&lt;/p&gt;

&lt;p&gt;What I want most is feedback — especially negative feedback.&lt;/p&gt;

&lt;p&gt;Questions I actually want answered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this solving a real problem, or mostly my problem?&lt;/li&gt;
&lt;li&gt;Is the “restrained alternative” framing actually valid?&lt;/li&gt;
&lt;li&gt;Which parts feel over-engineered?&lt;/li&gt;
&lt;li&gt;Which hosts are worth supporting, and which are not?&lt;/li&gt;
&lt;li&gt;Where does the abstraction break down?&lt;/li&gt;
&lt;li&gt;What should be removed or simplified?&lt;/li&gt;
&lt;li&gt;Where would this fail in your real workflow?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it and it feels wrong, that is useful.&lt;br&gt;
If you think the whole premise is flawed, that is useful too.&lt;br&gt;
If you think this should be much smaller, much dumber, or much more opinionated, that’s useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Please open issues if you test it
&lt;/h2&gt;

&lt;p&gt;The best outcome from posting this is not traffic.&lt;br&gt;
It’s not stars.&lt;br&gt;
It’s not generic “nice work.”&lt;/p&gt;

&lt;p&gt;The best outcome is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;issue reports&lt;/li&gt;
&lt;li&gt;criticism&lt;/li&gt;
&lt;li&gt;real-world friction reports&lt;/li&gt;
&lt;li&gt;product validation or invalidation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, please open an issue or drop feedback in the GitHub discussion.&lt;/p&gt;

&lt;p&gt;Feedback discussion: &lt;a href="https://github.com/ar27111994/agent-harness/discussions/126" rel="noopener noreferrer"&gt;https://github.com/ar27111994/agent-harness/discussions/126&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If possible, include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repo type/stack&lt;/li&gt;
&lt;li&gt;host used&lt;/li&gt;
&lt;li&gt;OS&lt;/li&gt;
&lt;li&gt;what you expected&lt;/li&gt;
&lt;li&gt;what actually happened&lt;/li&gt;
&lt;li&gt;what felt confusing, unnecessary, or weak&lt;/li&gt;
&lt;li&gt;whether the core idea itself seems useful or not&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your reaction is “this is overbuilt” or “this is the wrong abstraction,” please tell me.&lt;/p&gt;

&lt;p&gt;That’s honestly the kind of signal I’m looking for right now.&lt;/p&gt;




</description>
      <category>opensource</category>
      <category>ai</category>
      <category>typescript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Thu, 22 Jan 2026 16:21:28 +0000</pubDate>
      <link>https://dev.to/ar27111994/-55k4</link>
      <guid>https://dev.to/ar27111994/-55k4</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd" class="crayons-story__hidden-navigation-link"&gt;Fixing Google Antigravity Pro Authentication on Windows 10 + WSL2&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ar27111994" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3678450%2F7fe013eb-7f8e-407a-8400-ac85724d8a4f.jpeg" alt="ar27111994 profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ar27111994" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Ahmed Rehan
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Ahmed Rehan
                
              
              &lt;div id="story-author-preview-content-3188772" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ar27111994" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3678450%2F7fe013eb-7f8e-407a-8400-ac85724d8a4f.jpeg" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Ahmed Rehan&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jan 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd" id="article-link-3188772"&gt;
          Fixing Google Antigravity Pro Authentication on Windows 10 + WSL2
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vibecoding"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vibecoding&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/linux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;linux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/antigravity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;antigravity&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>programming</category>
      <category>vibecoding</category>
      <category>linux</category>
      <category>antigravity</category>
    </item>
    <item>
      <title>Fixing Google Antigravity Pro Authentication on Windows 10 + WSL2</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Wed, 21 Jan 2026 17:33:30 +0000</pubDate>
      <link>https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd</link>
      <guid>https://dev.to/ar27111994/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2-45jd</guid>
      <description>&lt;p&gt;Google Antigravity is Google's new agent-first IDE that promises powerful AI-assisted development. However, getting the Pro features to work on Windows 10 with WSL2 (Ubuntu 24.04.3 LTS) can be challenging due to authentication issues. This comprehensive guide will walk you through every step needed to get your Pro/Ultra subscription working seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When running Antigravity in WSL2, the authentication handshake between your Linux terminal and Windows browser often breaks or times out. This happens because WSL2 is a virtualized environment, and the redirect token struggles to find its way back to the WSL instance.&lt;/p&gt;

&lt;p&gt;Common symptoms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser opens but IDE stays on loading screen&lt;/li&gt;
&lt;li&gt;Browser Authentication is successful but the Agent Panel still says "Reqire Authentication" (or something similar), even though the &lt;code&gt;Antigravity Settings &amp;gt; Account&lt;/code&gt; Screen shows Account as already signed in.&lt;/li&gt;
&lt;li&gt;"Invalid Token" or "Login Expired" errors&lt;/li&gt;
&lt;li&gt;"Not Eligible" or "Unauthorized" messages&lt;/li&gt;
&lt;li&gt;Empty responses when running &lt;code&gt;agy&lt;/code&gt; commands&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows 10 with WSL2 installed&lt;/li&gt;
&lt;li&gt;Ubuntu 24.04.3 LTS (or similar)&lt;/li&gt;
&lt;li&gt;Google Antigravity installed on Windows&lt;/li&gt;
&lt;li&gt;A Google AI Pro or Ultra subscription using a personal Gmail account.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Fix the Silent Login Loop
&lt;/h2&gt;

&lt;p&gt;The first issue to address is WSL2's default NAT networking, which can hide the local ports Antigravity uses to listen for login signals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable Mirrored Networking
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;On Windows, navigate to &lt;code&gt;%USERPROFILE%&lt;/code&gt; (e.g., &lt;code&gt;C:\Users\YourName&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Create or edit a file named &lt;code&gt;.wslconfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add the following configuration:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;networkingMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mirrored&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Restart WSL by opening PowerShell as Administrator and running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--shutdown&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may start getting this message on WSL Startup, especially on Windows 10 (shouldn't theoretically appear on Windows 11):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl: Mirrored networking mode is not supported: Windows version 19045.6466 does not have the required features.
Falling back to NAT networking.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In which case, you should remove the unsupported setting from your global config to get rid of the warning every time you boot/reboot and open Ubuntu, as WSL2 cannot enable this mode and defaults back to the standard NAT (Network Address Translation).&lt;/p&gt;

&lt;p&gt;Instead, you can try the following config to ensure that Agents always reliably reach local addresses (Supported on Windows 10):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;localhostForwarding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add or ensure these lines exist in &lt;code&gt;%USERPROFILE%\.wslconfig&lt;/code&gt;. Save and run &lt;code&gt;wsl --shutdown&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Synchronize Your System Clock
&lt;/h2&gt;

&lt;p&gt;Google's OAuth 2.0 authentication is highly sensitive to time drift. WSL2 clocks often drift when a laptop sleeps (like in my case), causing tokens to be rejected as expired.&lt;/p&gt;

&lt;p&gt;Run this in your Ubuntu terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ntpdate time.windows.com

&lt;span class="c"&gt;# If you don't have ntpdate installed:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;util-linux-extra &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;hwclock &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Try Authentication again
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;If GUI login continues to fail, manually Sign out from your account in the &lt;code&gt;Antigravity Settings &amp;gt; Account&lt;/code&gt; Screen.&lt;/li&gt;
&lt;li&gt;Close and Restart the IDE.&lt;/li&gt;
&lt;li&gt;Click on the &lt;code&gt;Google Sign in&lt;/code&gt; button in the top right and Authorize your Google AI Pro account.&lt;/li&gt;
&lt;li&gt;Close the Antigravity Sign-in confirmation page.&lt;/li&gt;
&lt;li&gt;If the issue persists, repeat the first step and continue reading below.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4: Verify Account Eligibility
&lt;/h2&gt;

&lt;p&gt;Many authentication failures are actually due to account restrictions:&lt;/p&gt;

&lt;h3&gt;
  
  
  Account Type
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personal accounts&lt;/strong&gt; (&lt;code&gt;@gmail.com&lt;/code&gt;) have the highest success rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace accounts&lt;/strong&gt; may require admin approval for "Experimental Google AI Services"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Regional Matching
&lt;/h3&gt;

&lt;p&gt;Ensure your Windows Region, Google Account Region, and IP address (no VPN) all match. Mismatched regions can cause silent authentication failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Fix Missing WSL Extension Scripts
&lt;/h2&gt;

&lt;p&gt;A known bug in early Antigravity builds causes missing WSL helper scripts. Here's how to fix it:&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Script Extraction
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl" rel="noopener noreferrer"&gt;Remote - WSL extension&lt;/a&gt; from the VS Code Marketplace&lt;/li&gt;
&lt;li&gt;Click "Download VSIX" (downloads a &lt;code&gt;.vsix&lt;/code&gt; file) if in IDE extensions panel or use a tool like &lt;a href="https://cypherpunksamurai.github.io/vsix-downloader-webui/" rel="noopener noreferrer"&gt;VSIX Downloader&lt;/a&gt; for web download.&lt;/li&gt;
&lt;li&gt;Rename the file from &lt;code&gt;.vsix&lt;/code&gt; to &lt;code&gt;.zip&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open the zip and navigate to &lt;code&gt;extension/scripts/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy all files (especially &lt;code&gt;wslCode.sh&lt;/code&gt; and &lt;code&gt;wslDownload.sh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Paste into: &lt;code&gt;%LOCALAPPDATA%\Programs\Antigravity\resources\app\extensions\antigravity-remote-wsl\scripts\&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Fix the Launcher ID
&lt;/h3&gt;

&lt;p&gt;Even with scripts present, you need to update the extension ID reference:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;%LOCALAPPDATA%\Programs\Antigravity\bin\&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open the &lt;code&gt;antigravity&lt;/code&gt; file in Notepad&lt;/li&gt;
&lt;li&gt;Find: &lt;code&gt;WSL_EXT_ID="ms-vscode-remote.remote-wsl"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Change to: &lt;code&gt;WSL_EXT_ID="google.antigravity-remote-wsl"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Save and restart your WSL terminal&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test WSL Sign In (Command Palette Method)
&lt;/h3&gt;

&lt;p&gt;Try Authentication in WSL:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Antigravity on Windows&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl + Shift + P&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type "WSL" and select &lt;strong&gt;Remote-WSL: Connect to WSL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The bottom-left corner should show &lt;code&gt;[WSL: Ubuntu]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Try signing in from this connected state&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 6: Create the WSL-to-Windows Bridge
&lt;/h2&gt;

&lt;p&gt;To use &lt;code&gt;agy&lt;/code&gt; commands from your Ubuntu terminal, you need to create a proper bridge to the Windows binary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Wrapper Script
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create the local bin directory:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.local/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create the &lt;code&gt;agy&lt;/code&gt; wrapper (replace &lt;code&gt;[YOUR_WINDOWS_USERNAME]&lt;/code&gt; with your actual Windows username):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.local/bin/agy &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
#!/usr/bin/env bash
# Robust wrapper to bridge WSL -&amp;gt; Windows CMD -&amp;gt; Antigravity
# REPLACE [YOUR_WINDOWS_USERNAME] with your actual folder name

WIN_USER="[YOUR_WINDOWS_USERNAME]"
WIN_PATH="C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WIN_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;AppData&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;Local&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;Programs&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;Antigravity&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;bin&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;antigravity.cmd"

# Call via cmd.exe to ensure Windows environment is initialized
# cd to C: drive to avoid UNC path warnings
cd /mnt/c &amp;amp;&amp;amp; cmd.exe /c "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WIN_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;" "&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="sh"&gt;"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Make it executable:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.local/bin/agy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add to your PATH:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="$HOME/.local/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Your Windows Username
&lt;/h3&gt;

&lt;p&gt;Before running the wrapper, verify your path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /mnt/c/Users/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the &lt;code&gt;WIN_USER&lt;/code&gt; variable in the script to match exactly how your Windows username folder is spelled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable WSL Interoperability
&lt;/h3&gt;

&lt;p&gt;Ensure WSL can launch Windows processes by checking &lt;code&gt;/etc/wsl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/wsl.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it doesn't exist or is empty, create it with these settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[interop]&lt;/span&gt;
&lt;span class="py"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;appendWindowsPath&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart WSL with &lt;code&gt;wsl --shutdown&lt;/code&gt; from PowerShell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Verify Pro Authentication
&lt;/h2&gt;

&lt;p&gt;Once everything is set up, verify your CLI Setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check version&lt;/span&gt;
agy &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# List available commands&lt;/span&gt;
agy &lt;span class="nt"&gt;--help&lt;/span&gt;

&lt;span class="c"&gt;# Open IDE in current directory&lt;/span&gt;
agy &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try signing in again to your Google account. The &lt;code&gt;Agent Panel&lt;/code&gt; should be available now, and the &lt;code&gt;Antigravity Settings &amp;gt; Account&lt;/code&gt; Screen shows Account as already signed in. Expected output for &lt;code&gt;Agent Panel&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tier:&lt;/strong&gt; Pro or Ultra&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Gemini 3 Pro or Gemini 3 Flash, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Limit:&lt;/strong&gt; 1M+ tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Pro Agents
&lt;/h3&gt;

&lt;p&gt;Now you can invoke high-level agents directly on your WSL files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agy chat &lt;span class="s2"&gt;"Analyze the structure of this repo and suggest refactoring opportunities"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because you're authenticated as Pro, the agent will use the full index to understand your entire codebase structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;Browser opens, IDE stays loading&lt;/td&gt;
&lt;td&gt;Enable &lt;code&gt;networkingMode=mirrored&lt;/code&gt; in &lt;code&gt;.wslconfig&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clock Drift&lt;/td&gt;
&lt;td&gt;"Invalid Token" or "Login Expired"&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;sudo hwclock -s&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account Type&lt;/td&gt;
&lt;td&gt;"Not Eligible"&lt;/td&gt;
&lt;td&gt;Use personal &lt;code&gt;@gmail.com&lt;/code&gt; account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missing Scripts&lt;/td&gt;
&lt;td&gt;IDE won't connect to WSL&lt;/td&gt;
&lt;td&gt;Download and extract scripts from VS Code Marketplace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong Extension ID&lt;/td&gt;
&lt;td&gt;Connection fails silently&lt;/td&gt;
&lt;td&gt;Update &lt;code&gt;WSL_EXT_ID&lt;/code&gt; in launcher script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UNC Path Warnings&lt;/td&gt;
&lt;td&gt;Warnings in terminal output&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;cd /mnt/c &amp;amp;&amp;amp;&lt;/code&gt; in wrapper script&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Common Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Open current directory in Antigravity&lt;/span&gt;
agy &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Check process usage and diagnostic information&lt;/span&gt;
agy &lt;span class="nt"&gt;--status&lt;/span&gt;

&lt;span class="c"&gt;# Check version&lt;/span&gt;
agy &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Get help&lt;/span&gt;
agy &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pro Tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistent Sessions:&lt;/strong&gt; Enable "Persistent Agent" in Antigravity UI to keep agents running even if you close the terminal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Project Indexing:&lt;/strong&gt; Let Antigravity fully index your project before asking complex questions for better results&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token Limits:&lt;/strong&gt; Pro users get 1M+ token context windows, perfect for analyzing large codebases&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regional Consistency:&lt;/strong&gt; Avoid using VPNs during authentication to prevent region mismatch issues&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Getting Google Antigravity Pro working on WSL2 requires bridging the gap between the Windows and Linux environments. By following these steps—fixing networking, synchronizing clocks, creating proper symlinks, and enabling interoperability—you'll unlock the full power of Google's agentic IDE.&lt;/p&gt;

&lt;p&gt;The effort is worth it: once configured, you'll have access to powerful AI agents that can understand and refactor your entire codebase, all while working seamlessly in your WSL2 development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=example" rel="noopener noreferrer"&gt;Google Antigravity Setup Guide (Video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl" rel="noopener noreferrer"&gt;VS Code Remote - WSL Extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/windows/wsl/" rel="noopener noreferrer"&gt;WSL2 Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Last updated: January 2026 | Tested on Windows 10 + WSL2 Ubuntu 24.04.3 LTS&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>vibecoding</category>
      <category>linux</category>
      <category>antigravity</category>
    </item>
    <item>
      <title>Stop paying for webhook debuggers. I built a better one (Open Source).</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Thu, 15 Jan 2026 13:40:27 +0000</pubDate>
      <link>https://dev.to/ar27111994/stop-paying-for-webhook-debuggers-i-built-a-better-one-open-source-dcl</link>
      <guid>https://dev.to/ar27111994/stop-paying-for-webhook-debuggers-i-built-a-better-one-open-source-dcl</guid>
      <description>&lt;h3&gt;
  
  
  The Struggle
&lt;/h3&gt;

&lt;p&gt;I remember the payment bug that kept me up until 3 AM.&lt;br&gt;
Stripe was sending a &lt;code&gt;invoice.payment_failed&lt;/code&gt; webhook, but only in production.&lt;br&gt;
I checked my logs: &lt;em&gt;Truncated&lt;/em&gt;.&lt;br&gt;
I checked my tunneling tool: &lt;em&gt;Session expired&lt;/em&gt;.&lt;br&gt;
I checked my SaaS bin: &lt;em&gt;History limit reached&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I realized I didn't have a debugging tool; I had a toy.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Solution: Webhook Debugger
&lt;/h3&gt;

&lt;p&gt;I decided to build my own solution. But I didn't just want a "bucket" that catches requests. I wanted to build a &lt;strong&gt;Reference Implementation&lt;/strong&gt; for how a modern, secure Node.js application should look in 2026.&lt;/p&gt;

&lt;p&gt;Here are the &lt;strong&gt;13 Engineering Patterns&lt;/strong&gt; I used to build it:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Global SSE Heartbeat &amp;amp; Padding 💓
&lt;/h4&gt;

&lt;p&gt;Most SSE implementations leak memory by creating a timer per connection.&lt;br&gt;
&lt;strong&gt;My Approach&lt;/strong&gt;: A single &lt;code&gt;setInterval&lt;/code&gt; iterates a &lt;code&gt;Set&lt;/code&gt; of clients.&lt;br&gt;
&lt;strong&gt;The Pro Tip&lt;/strong&gt;: I added &lt;code&gt;res.write(' '.repeat(2048))&lt;/code&gt; (2KB whitespace) and &lt;code&gt;X-Accel-Buffering: no&lt;/code&gt; headers. Why? Because corporate firewalls (and Nginx) love buffering streams. The padding forces them to flush the connection immediately.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. SSRF Protection (DNS &amp;amp; IP Verification) 🛡️
&lt;/h4&gt;

&lt;p&gt;Allowing user-defined webhooks is dangerous (Server-Side Request Forgery).&lt;br&gt;
&lt;strong&gt;The Fix&lt;/strong&gt;: I wrote a custom validator in &lt;code&gt;src/utils/ssrf.js&lt;/code&gt; that resolves the DNS &lt;em&gt;before&lt;/em&gt; the request. It checks the IP against a blocklist of private ranges (RFC 1918) and cloud metadata services (&lt;code&gt;169.254.169.254&lt;/code&gt;). It even handles IPv4-mapped IPv6 addresses (&lt;code&gt;::ffff:127.0.0.1&lt;/code&gt;).&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Deep Replay with Exponential Backoff 🔄
&lt;/h4&gt;

&lt;p&gt;Retrying a failed webhook isn't just "try again".&lt;br&gt;
&lt;strong&gt;The Logic&lt;/strong&gt;: If the destination yields a transient error (&lt;code&gt;ECONNABORTED&lt;/code&gt;, &lt;code&gt;503&lt;/code&gt;), the system waits 1s, then 2s, then 4s.&lt;br&gt;
&lt;strong&gt;Header Stripping&lt;/strong&gt;: The replay engine automatically strips sensitive headers (&lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Cookie&lt;/code&gt;) so you don't accidentally send production credentials to your local dev environment.&lt;/p&gt;
&lt;h4&gt;
  
  
  4. Timing-Safe Authentication ⏱️
&lt;/h4&gt;

&lt;p&gt;Never compare API keys with &lt;code&gt;===&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;The Attack&lt;/strong&gt;: An attacker can measure how long your server takes to say "No" to guess the key character-by-character.&lt;br&gt;
&lt;strong&gt;The Fix&lt;/strong&gt;: I use &lt;code&gt;crypto.timingSafeEqual&lt;/code&gt; in &lt;code&gt;src/utils/auth.js&lt;/code&gt; to ensure the comparison takes the exact same time whether the key is 99% correct or 0% correct.&lt;/p&gt;
&lt;h4&gt;
  
  
  5. Memory-Safe Rate Limiting (LRU) 🧠
&lt;/h4&gt;

&lt;p&gt;Standard rate limiters are often purely in-memory maps. If a botnet hits you with 1 million IPs, your server crashes (OOM).&lt;br&gt;
&lt;strong&gt;The Pattern&lt;/strong&gt;: My &lt;code&gt;RateLimiter&lt;/code&gt; uses a &lt;strong&gt;Sliding Window&lt;/strong&gt; with &lt;strong&gt;LRU Eviction&lt;/strong&gt;. It hard-caps at 1,000 entries. If the map is full, the oldest IP is evicted to make room. It prioritizes stability over strictness.&lt;/p&gt;
&lt;h4&gt;
  
  
  6. Memory-Safe Dataset Filtering 🔍
&lt;/h4&gt;

&lt;p&gt;Searching for a single timestamp in a 1GB JSON dataset will crash a standard Node.js process.&lt;br&gt;
&lt;strong&gt;The Solution&lt;/strong&gt;: Iterative Pagination. The &lt;code&gt;/replay&lt;/code&gt; endpoint reads chunks of 1000 items (&lt;code&gt;dataset.getData({ limit, offset })&lt;/code&gt;), searches for the event ID, and fetches the next chunk only if not found. This ensures we never load the entire dataset into memory.&lt;/p&gt;
&lt;h4&gt;
  
  
  7. Input Sanitization &amp;amp; Coercion 🧹
&lt;/h4&gt;

&lt;p&gt;Inputs from the wild are messy. Strings look like numbers; booleans look like strings.&lt;br&gt;
&lt;strong&gt;The Pattern&lt;/strong&gt;: A dedicated &lt;code&gt;coerceRuntimeOptions&lt;/code&gt; utility in &lt;code&gt;src/utils/config.js&lt;/code&gt; recursively walks the input object, coercing &lt;code&gt;"true"&lt;/code&gt; -&amp;gt; &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;"5"&lt;/code&gt; -&amp;gt; &lt;code&gt;5&lt;/code&gt;, ensuring the runtime configuration isn't crashed by type mismatches.&lt;/p&gt;
&lt;h4&gt;
  
  
  8. Index.html Caching 🚀
&lt;/h4&gt;

&lt;p&gt;We serve a UI, but we aren't a CDN.&lt;br&gt;
&lt;strong&gt;The Optimization&lt;/strong&gt;: The &lt;code&gt;index.html&lt;/code&gt; template is read from disk &lt;em&gt;once&lt;/em&gt; at startup and cached in a string variable (&lt;code&gt;indexTemplate&lt;/code&gt;). Placeholders like &lt;code&gt;{{VERSION}}&lt;/code&gt; are replaced on-the-fly using &lt;code&gt;escapeHtml()&lt;/code&gt;, but the disk I/O cost is paid only once.&lt;/p&gt;
&lt;h4&gt;
  
  
  9. Bootstrap Validation Logic 🩹
&lt;/h4&gt;

&lt;p&gt;What happens if the user manually edits the &lt;code&gt;INPUT.json&lt;/code&gt; and breaks the JSON syntax?&lt;br&gt;
&lt;strong&gt;Self-Healing&lt;/strong&gt;: The &lt;code&gt;ensureLocalInputExists&lt;/code&gt; function in &lt;code&gt;src/utils/bootstrap.js&lt;/code&gt; detects corrupt JSON on startup. Instead of crashing, it automatically renames the bad file to &lt;code&gt;.tmp&lt;/code&gt; and writes a fresh default configuration, logging a warning. The app &lt;em&gt;always&lt;/em&gt; starts.&lt;/p&gt;
&lt;h4&gt;
  
  
  10. Optimized Headers ⚡
&lt;/h4&gt;

&lt;p&gt;Every millisecond counts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Content-Encoding: identity&lt;/code&gt;: Disables gzip for the SSE stream (gzip buffers, which kills real-time).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cache-Control: no-cache&lt;/code&gt;: Forces browsers to verify the stream status.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Connection: keep-alive&lt;/code&gt;: Critical for long-lived streams.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  11. Testing Asynchronous Code 🧪
&lt;/h4&gt;

&lt;p&gt;Testing streaming and retries is notoriously hard.&lt;br&gt;
&lt;strong&gt;The Strategy&lt;/strong&gt;: We use &lt;code&gt;jest&lt;/code&gt; with custom helpers (&lt;code&gt;waitForCondition&lt;/code&gt; in &lt;code&gt;tests/helpers/test-utils.js&lt;/code&gt;) and mocked timers. In &lt;code&gt;resilience.test.js&lt;/code&gt;, we mock &lt;code&gt;axios&lt;/code&gt; to fail exactly twice with &lt;code&gt;ECONNABORTED&lt;/code&gt; to verify the retry logic attempts exactly 3 times before giving up.&lt;/p&gt;
&lt;h4&gt;
  
  
  12. Hot-Reloading (Zero Downtime Config) 🔥
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: Restarting the server just to change the API key or add a webhook URL loses all SSE connections.&lt;br&gt;
&lt;strong&gt;The Solution&lt;/strong&gt;: A background poller in &lt;code&gt;src/main.js&lt;/code&gt; reads the &lt;code&gt;INPUT.json&lt;/code&gt; from the Key-Value Store every 5 seconds. When a change is detected:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It &lt;strong&gt;diffs&lt;/strong&gt; the new config against the old one.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;updates&lt;/strong&gt; middleware (body parser limits, rate limiter), auth keys, and webhook counts dynamically.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;reconciles&lt;/strong&gt; the webhook IDs: if the user increased &lt;code&gt;urlCount&lt;/code&gt;, new IDs are generated; if decreased, no IDs are removed to prevent data loss.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is all enabled by the &lt;code&gt;loggerMiddleware.updateOptions()&lt;/code&gt; function, which allows runtime reconfiguration of the logger instance.&lt;/p&gt;
&lt;h4&gt;
  
  
  13. Escape the "SaaS Tax" (Self-Hosting) 💸
&lt;/h4&gt;

&lt;p&gt;If you are an agency handling 50 clients, paying $30/mo per seat for debugging tools adds up.&lt;br&gt;
Since this is a standard &lt;strong&gt;Dockerized Node.js app&lt;/strong&gt;, you can deploy it to your own generic VPS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; apify/actor-node:20&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; npm start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ar27111994/webhook-debugger-logger" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/strong&gt; (v2.8.7 is out now!)&lt;/p&gt;




</description>
      <category>webhooks</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How to Debug Webhook Integrations in Minutes (Step-by-Step Guide)</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Thu, 25 Dec 2025 15:15:33 +0000</pubDate>
      <link>https://dev.to/ar27111994/how-to-debug-webhook-integrations-in-minutes-step-by-step-guide-3ccf</link>
      <guid>https://dev.to/ar27111994/how-to-debug-webhook-integrations-in-minutes-step-by-step-guide-3ccf</guid>
      <description>&lt;p&gt;Debugging webhooks is painful. You can't see what's being sent, signature verification fails mysteriously, and testing edge cases requires complex setups.&lt;/p&gt;

&lt;p&gt;In this tutorial, I'll show you how to debug webhook integrations in minutes using Webhook Debugger &amp;amp; Logger.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Accomplish
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial, you'll:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Capture webhook requests from any service (Stripe, Shopify, etc.)&lt;/li&gt;
&lt;li&gt;✅ Use &lt;strong&gt;"Launch Packs"&lt;/strong&gt; for instant, pre-configured setup&lt;/li&gt;
&lt;li&gt;✅ Inspect headers, body, and metadata in real-time&lt;/li&gt;
&lt;li&gt;✅ Verify signatures with &lt;strong&gt;Raw Request Data&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Replay requests to test idempotency&lt;/li&gt;
&lt;li&gt;✅ Secure your logs with &lt;strong&gt;CIDR/Bearer Auth&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⏱️ &lt;strong&gt;Time required:&lt;/strong&gt; ~10 minutes&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Free Apify account (sign up with email or GitHub)&lt;/li&gt;
&lt;li&gt;A service that sends webhooks (Stripe, GitHub, Shopify, etc.)&lt;/li&gt;
&lt;li&gt;No coding required for basic usage&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What You'll Achieve
&lt;/h2&gt;

&lt;p&gt;By the end, you'll have a complete webhook debugging workflow that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Captures every incoming request&lt;/li&gt;
&lt;li&gt;Shows raw data for signature debugging&lt;/li&gt;
&lt;li&gt;Allows you to replay requests for testing&lt;/li&gt;
&lt;li&gt;Exports logs as JSON/CSV&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real example: I debugged a Stripe payment webhook in 8 minutes using this approach. Previously took me 3+ hours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Access Webhook Debugger &amp;amp; Logger
&lt;/h2&gt;

&lt;p&gt;Go to the &lt;a href="https://apify.com/ar27111994/webhook-debugger-logger" rel="noopener noreferrer"&gt;Webhook Debugger page&lt;/a&gt; on Apify Store.&lt;/p&gt;

&lt;p&gt;If you don't have an Apify account:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Try for free"&lt;/li&gt;
&lt;li&gt;Sign up with email or GitHub (takes 30 seconds)&lt;/li&gt;
&lt;li&gt;You'll be redirected to Apify Console&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What to expect:&lt;/strong&gt; You'll see the Actor's input configuration page.&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%2Fpdxvxj6lnqqw0g22c8m6.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%2Fpdxvxj6lnqqw0g22c8m6.png" alt="Screenshot showing Actor home page" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Configure Basic Settings
&lt;/h2&gt;

&lt;p&gt;You'll see the input form with these key fields:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;URL Count&lt;/strong&gt; (required)
&lt;/h3&gt;

&lt;p&gt;How many webhook URLs to generate (1-10)&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;"urlCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&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;p&gt;Example: If you're testing Stripe payments, GitHub webhooks, and Shopify orders, set this to 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Launch Pack Settings&lt;/strong&gt; (v2.7.0 Optimized)
&lt;/h3&gt;

&lt;p&gt;Choose a pre-configured scenario for your stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shopify Launch Pack&lt;/strong&gt;: 72h retention + sub-10ms logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe Hardening Pack&lt;/strong&gt;: RAW body capture for signature validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack Block Kit&lt;/strong&gt;: Interaction mocking and payload validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Basic configuration example:
&lt;/h3&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;"urlCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retentionHours"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&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;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%2Frtnsliboa12w10e3fzfl.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%2Frtnsliboa12w10e3fzfl.png" alt="Screenshot showing default Actor Input options" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Advanced Configuration (Optional)
&lt;/h2&gt;

&lt;p&gt;For more control, expand the advanced settings:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Max Payload Size&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Maximum request body size (default: 10MB)&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;"maxPayloadSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10485760&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;h3&gt;
  
  
  &lt;strong&gt;Enable JSON Parsing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Automatically parse JSON payloads (default: true)&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;"enableJSONParsing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;h3&gt;
  
  
  &lt;strong&gt;JSON Schema Validation&lt;/strong&gt; (v2.0+)
&lt;/h3&gt;

&lt;p&gt;Reject invalid payloads automatically:&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;"jsonSchema"&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;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&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="s2"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"event"&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;"string"&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;"amount"&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;"number"&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;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; Testing API integrations that must conform to a specific payload structure.&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%2F3pxb7kqxl39qwgilrd8s.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%2F3pxb7kqxl39qwgilrd8s.png" alt="Screenshot showing Advanced input options filled in" width="800" height="933"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Run the Actor
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;"Start"&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Status changes to &lt;strong&gt;"Running"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Typically takes 5-10 seconds to spin up&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Tip:&lt;/strong&gt; You can close the page - the Actor continues running in the background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's happening:&lt;/strong&gt; The Actor is starting a serverless container and generating your webhook URLs.&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%2F8j2hdnwnl11upp0y93a0.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%2F8j2hdnwnl11upp0y93a0.png" alt="📸 Screenshot: Run status page showing " width="800" height="899"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Get Your Webhook URLs
&lt;/h2&gt;

&lt;p&gt;Once running, there are two ways to get your URLs:&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 1: Live View
&lt;/h3&gt;

&lt;p&gt;Click &lt;strong&gt;"Live View"&lt;/strong&gt; tab to see the web interface with your URLs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 2: Key-Value Store
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Storage&lt;/strong&gt; → &lt;strong&gt;Key-value stores&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Look for &lt;strong&gt;"WEBHOOK_STATE"&lt;/strong&gt; key&lt;/li&gt;
&lt;li&gt;You'll see your webhook IDs&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  URL Format
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;actor-run-id&amp;gt;.runs.apify.net/webhook/&amp;lt;webhook-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://abc123xyz.runs.apify.net/webhook/wh_abc123
https://abc123xyz.runs.apify.net/webhook/wh_def456
https://abc123xyz.runs.apify.net/webhook/wh_ghi789
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;💡 Pro tip:&lt;/strong&gt; Bookmark these URLs for the duration of your testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Configure Your Service
&lt;/h2&gt;

&lt;p&gt;Now configure your webhook source (Stripe, GitHub, etc.) to send to your URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Stripe
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Stripe Dashboard → Developers → Webhooks&lt;/li&gt;
&lt;li&gt;Click "Add endpoint"&lt;/li&gt;
&lt;li&gt;Paste your webhook URL: &lt;code&gt;https://&amp;lt;run-id&amp;gt;.runs.apify.net/webhook/wh_abc123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select events (e.g., &lt;code&gt;payment_intent.succeeded&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example: GitHub
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Repository → Settings → Webhooks&lt;/li&gt;
&lt;li&gt;Click "Add webhook"&lt;/li&gt;
&lt;li&gt;Paste your URL&lt;/li&gt;
&lt;li&gt;Select events (e.g., &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;pull_request&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What happens next:&lt;/strong&gt; Any webhook sent to this URL will be captured and logged.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Trigger a Test Webhook
&lt;/h2&gt;

&lt;p&gt;There are two ways to test:&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 1: Service Test Button
&lt;/h3&gt;

&lt;p&gt;Most services have a "Send test webhook" button. Use it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 2: Manual cURL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;run-id&amp;gt;.runs.apify.net/webhook/wh_abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"event": "payment.success", "userId": "user_123", "amount": 9999}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you'll see:&lt;/strong&gt; Request appears in the Dataset within 1-2 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: View Results in Real-Time
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Dataset Tab
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Storage&lt;/strong&gt; → &lt;strong&gt;Dataset&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;See all captured requests&lt;/li&gt;
&lt;li&gt;Click any item to see full details&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Option 2: Real-time Streaming (Advanced)
&lt;/h3&gt;

&lt;p&gt;Use Server-Sent Events (SSE) for live monitoring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-N&lt;/span&gt; https://&amp;lt;run-id&amp;gt;.runs.apify.net/log-stream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You'll See
&lt;/h3&gt;

&lt;p&gt;Each captured request includes:&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;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-12-25T14:31:45Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"webhookId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wh_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&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;"content-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;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user-agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stripe/1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stripe-signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t=1735137105,v1=abc123..."&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;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;payment.success&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;amount&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 9999}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"queryParams"&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;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contentType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"processingTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remoteIp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"54.187.174.169"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&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;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%2F4tz22j9gj3ockzsv344c.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%2F4tz22j9gj3ockzsv344c.png" alt="📸 Screenshot: Results preview with data visible" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Debug Signature Verification
&lt;/h2&gt;

&lt;p&gt;This is where raw data access becomes crucial.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Signature verification fails because you're comparing the signature against the PARSED JSON, not the raw bytes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Use the captured raw body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// From Webhook Debugger logs:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"type": "payment.success", "amount": 9999}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;t=1735137105,v1=abc123...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// from stripe-signature header&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whsec_yourStripeSecret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Extract timestamp and signature&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Compute expected signature&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Verify&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedSignature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Signature valid:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;💡 Key insight:&lt;/strong&gt; You MUST use the raw body from the logs, not your parsed JSON object.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: Test Idempotency with Replay
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;/replay&lt;/code&gt; API to test duplicate event handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;run-id&amp;gt;.runs.apify.net/replay/wh_abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "destination": "https://myapp.com/webhook",
    "headers": {
      "X-Test-Replay": "true"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This resends the captured webhook to your destination, allowing you to verify that your app handles duplicates correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 11: Export and Analyze
&lt;/h2&gt;

&lt;p&gt;Export your logs for further analysis:&lt;/p&gt;

&lt;h3&gt;
  
  
  Via Console:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Storage&lt;/strong&gt; → &lt;strong&gt;Dataset&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Export"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose format (JSON, CSV, Excel)&lt;/li&gt;
&lt;li&gt;Download&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Via API:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.apify.com/v2/datasets/&amp;lt;dataset-id&amp;gt;/items?format=json&amp;amp;clean=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;your-api-token&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use cases for exported data:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share with team members&lt;/li&gt;
&lt;li&gt;Import into analytics tools&lt;/li&gt;
&lt;li&gt;Create custom reports&lt;/li&gt;
&lt;li&gt;Archive for compliance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Pro Tips for Better Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Use Specific Webhook IDs
&lt;/h3&gt;

&lt;p&gt;Create different IDs for different services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wh_stripe&lt;/code&gt; for Stripe&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wh_github&lt;/code&gt; for GitHub&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wh_shopify&lt;/code&gt; for Shopify&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Filter Logs
&lt;/h3&gt;

&lt;p&gt;Use query parameters to filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/logs?webhookId&lt;span class="o"&gt;=&lt;/span&gt;wh_stripe&amp;amp;method&lt;span class="o"&gt;=&lt;/span&gt;POST&amp;amp;statusCode&lt;span class="o"&gt;=&lt;/span&gt;200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Force Status Codes
&lt;/h3&gt;

&lt;p&gt;Test error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&amp;lt;run-id&amp;gt;.runs.apify.net/webhook/wh_abc123?__status&lt;span class="o"&gt;=&lt;/span&gt;500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Simulate Latency (v2.0+)
&lt;/h3&gt;

&lt;p&gt;Test timeout handling:&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;"customResponseDelay"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;delay&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;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue: "Webhook not found or expired"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The webhook ID is correct&lt;/li&gt;
&lt;li&gt;The Actor is still running&lt;/li&gt;
&lt;li&gt;The retention period hasn't expired&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use &lt;code&gt;/info&lt;/code&gt; endpoint to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://&amp;lt;run-id&amp;gt;.runs.apify.net/info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue: Requests not appearing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify the URL is correct&lt;/li&gt;
&lt;li&gt;Check that the Actor is in "Running" state&lt;/li&gt;
&lt;li&gt;Ensure the service is actually sending webhooks&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Issue: JSON parsing errors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check the &lt;code&gt;enableJSONParsing&lt;/code&gt; setting and verify the payload is valid JSON.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You now have a complete webhook debugging workflow! 🎉&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you learned:&lt;/strong&gt;&lt;br&gt;
✅ How to capture webhooks without localhost tunneling&lt;br&gt;
✅ How to inspect raw request data for signature debugging&lt;br&gt;
✅ How to replay webhooks to test idempotency&lt;br&gt;
✅ How to export and analyze webhook logs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; What used to take 3-4 hours now takes 10-15 minutes.&lt;/p&gt;

&lt;p&gt;Try Webhook Debugger yourself: &lt;a href="https://apify.com/ar27111994/webhook-debugger-logger" rel="noopener noreferrer"&gt;https://apify.com/ar27111994/webhook-debugger-logger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions?&lt;/strong&gt; Drop them in the comments! 👇&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;👍 If this tutorial helped you, please give it a like and follow for more webhook debugging tips!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webhooks</category>
      <category>api</category>
      <category>debugging</category>
    </item>
    <item>
      <title>The 6 Best Webhook Testing Tools for Developers in 2025</title>
      <dc:creator>Ahmed Rehan</dc:creator>
      <pubDate>Thu, 25 Dec 2025 15:09:49 +0000</pubDate>
      <link>https://dev.to/ar27111994/the-6-best-webhook-testing-tools-for-developers-in-2025-1o0i</link>
      <guid>https://dev.to/ar27111994/the-6-best-webhook-testing-tools-for-developers-in-2025-1o0i</guid>
      <description>&lt;p&gt;Webhooks power modern web apps, but when they fail, debugging becomes a nightmare. After testing dozens of tools while building integrations for Stripe, GitHub, and Shopify, here are the 6 best webhook testing tools for developers in 2025.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🥇 &lt;strong&gt;Best overall&lt;/strong&gt;: Webhook Debugger &amp;amp; Logger (pay-per-event, great features)&lt;/li&gt;
&lt;li&gt;🏠 &lt;strong&gt;Best for localhost&lt;/strong&gt;: ngrok (tunneling classic)&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Best for quick tests&lt;/strong&gt;: Webhook.site (free, simple)&lt;/li&gt;
&lt;li&gt;🏢 &lt;strong&gt;Best for enterprise&lt;/strong&gt;: Hookdeck (production-grade, expensive)&lt;/li&gt;
&lt;li&gt;💰 &lt;strong&gt;Best free option&lt;/strong&gt;: RequestBin (basic but gets the job done)&lt;/li&gt;
&lt;li&gt;🎭 &lt;strong&gt;Best for mocking&lt;/strong&gt;: Beeceptor (custom API responses)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I Looked For
&lt;/h2&gt;

&lt;p&gt;Before diving in, here's what matters when choosing a webhook testing tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; - How fast can you start capturing webhooks?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of integration&lt;/strong&gt; - Simple setup or complex configuration?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; - Clear docs make or break dev experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing&lt;/strong&gt; - Free tier? Subscription? Pay-per-use?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community/Support&lt;/strong&gt; - Active development and responsive support?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's look at each tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🥇 Webhook Debugger &amp;amp; Logger
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://apify.com/ar27111994/webhook-debugger-logger" rel="noopener noreferrer"&gt;https://apify.com/ar27111994/webhook-debugger-logger&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pricing:&lt;/strong&gt; $10/1,000 webhooks (pay-per-event)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Quick start difficulty:&lt;/strong&gt; Easy ✅&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;br&gt;
A serverless Apify Actor designed specifically for webhook debugging. Generates temporary webhook URLs and logs every incoming request with complete metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;✅ CIDR IP Whitelisting &amp;amp; Bearer Auth 🛡️
✅ Sensitive Header Masking (Auth/Key scrubbing)
✅ Real-time request capture (SSE Stream)
✅ No localhost tunnelling needed
✅ /replay API for testing idempotency
✅ JSON Schema validation
✅ Custom status codes &amp;amp; latency simulation (Standby Mode)
✅ Export as JSON/CSV
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Developer experience highlights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero setup - start capturing in 30 seconds&lt;/li&gt;
&lt;li&gt;Complete raw data access (crucial for signature debugging)&lt;/li&gt;
&lt;li&gt;Programmatic API for CI/CD integration&lt;/li&gt;
&lt;li&gt;Enterprise features (IP whitelisting, API key auth)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch Week Survival&lt;/strong&gt;: Handling burst traffic for high-stakes releases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe &amp;amp; Shopify integrations&lt;/strong&gt;: Using pre-configured "Launch Packs."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing signature verification&lt;/strong&gt;: Accessing raw request bodies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Security&lt;/strong&gt;: CIDR whitelisting and header scrubbing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API mocking&lt;/strong&gt;: Simulating slow or faulty 3rd party APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start Actor and get webhook URL&lt;/span&gt;
&lt;span class="c"&gt;# https://&amp;lt;run-id&amp;gt;.runs.apify.net/webhook/wh_abc123&lt;/span&gt;

&lt;span class="c"&gt;# Send test webhook&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;run-id&amp;gt;.runs.apify.net/webhook/wh_abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"event": "payment.success", "amount": 9999}'&lt;/span&gt;

&lt;span class="c"&gt;# Replay captured webhook&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;run-id&amp;gt;.runs.apify.net/replay/wh_abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"destination": "https://myapp.com/webhook"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay only for what you use&lt;/li&gt;
&lt;li&gt;Advanced features (replay, mocking, validation)&lt;/li&gt;
&lt;li&gt;Persistent URLs (1-72 hours configurable)&lt;/li&gt;
&lt;li&gt;No tunnelling complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires Apify account (free tier available)&lt;/li&gt;
&lt;li&gt;Not as well-known as ngrok&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. 🏠 ngrok
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;https://ngrok.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pricing:&lt;/strong&gt; Free / $8/month (Basic) / $20/month (Pro)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Quick start difficulty:&lt;/strong&gt; Moderate&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;br&gt;
Creates secure tunnels to expose your localhost to the internet. The gold standard for local webhook development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best feature:&lt;/strong&gt; Request inspection with replay&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install ngrok&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;ngrok

&lt;span class="c"&gt;# Start tunnel to localhost:3000&lt;/span&gt;
ngrok http 3000

&lt;span class="c"&gt;# Get public URL like: https://abc123.ngrok.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Industry standard&lt;/li&gt;
&lt;li&gt;Robust and reliable&lt;/li&gt;
&lt;li&gt;Request inspection included&lt;/li&gt;
&lt;li&gt;Replay functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free URLs change every session&lt;/li&gt;
&lt;li&gt;Requires CLI installation&lt;/li&gt;
&lt;li&gt;Monthly subscription for persistent URLs&lt;/li&gt;
&lt;li&gt;Focused on tunnelling, not debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Local development when you need webhooks to hit your localhost server.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. 🎯 Webhook.site
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://webhook.site" rel="noopener noreferrer"&gt;https://webhook.site&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pricing:&lt;/strong&gt; Free (limited) / $10/month (Pro)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Quick start difficulty:&lt;/strong&gt; Easy ✅&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;br&gt;
Instant webhook URLs for quick inspections. No account needed (free tier).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best feature:&lt;/strong&gt; Instant setup - open site, get URL&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero setup on free tier&lt;/li&gt;
&lt;li&gt;Clean, simple UI&lt;/li&gt;
&lt;li&gt;Good for quick tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some features require Custom Actions (paid)&lt;/li&gt;
&lt;li&gt;Subscription-based pricing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; One-off tests when you just need to see what's being sent.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. 🏢 Hookdeck
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://hookdeck.com" rel="noopener noreferrer"&gt;https://hookdeck.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pricing:&lt;/strong&gt; Free tier / $70/month (Pro)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Quick start difficulty:&lt;/strong&gt; Complex&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;br&gt;
Enterprise webhook management platform with routing, retries, and transformation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best feature:&lt;/strong&gt; Production-grade reliability with automatic retries&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built for production scale&lt;/li&gt;
&lt;li&gt;Automatic retries and error handling&lt;/li&gt;
&lt;li&gt;Payload transformation&lt;/li&gt;
&lt;li&gt;Extensive logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expensive ($70/month minimum)&lt;/li&gt;
&lt;li&gt;Overkill for simple debugging&lt;/li&gt;
&lt;li&gt;Complex setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Enterprise teams managing thousands of webhooks daily.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 💰 RequestBin
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://requestbin.com" rel="noopener noreferrer"&gt;https://requestbin.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pricing:&lt;/strong&gt; Free (hosted) / Self-hosted&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Quick start difficulty:&lt;/strong&gt; Easy ✅&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;br&gt;
Creates temporary bins to capture HTTP requests. Minimalist and straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best feature:&lt;/strong&gt; Completely free, no account required&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free (hosted version)&lt;/li&gt;
&lt;li&gt;Open source&lt;/li&gt;
&lt;li&gt;Simple interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URLs expire quickly (20 requests or 48 hours)&lt;/li&gt;
&lt;li&gt;Very basic features&lt;/li&gt;
&lt;li&gt;No replay or mocking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Simple, disposable testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. 🎭 Beeceptor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://beeceptor.com" rel="noopener noreferrer"&gt;https://beeceptor.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pricing:&lt;/strong&gt; Free tier / $10/month (Pro)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Quick start difficulty:&lt;/strong&gt; Easy&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;br&gt;
Mock API endpoints with custom responses. Useful for testing client behaviour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best feature:&lt;/strong&gt; Custom response rules&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Great for API mocking&lt;/li&gt;
&lt;li&gt;Custom responses&lt;/li&gt;
&lt;li&gt;Request logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not webhook-specific&lt;/li&gt;
&lt;li&gt;Limited free tier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; API mocking when you need custom responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Webhook Debugger&lt;/th&gt;
&lt;th&gt;ngrok&lt;/th&gt;
&lt;th&gt;Webhook.site&lt;/th&gt;
&lt;th&gt;Hookdeck&lt;/th&gt;
&lt;th&gt;RequestBin&lt;/th&gt;
&lt;th&gt;Beeceptor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replay&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mocking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema Validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅*&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;URL Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1-72h&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;168h (7 days)&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;48h&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free Tier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price (Paid)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$10/1k events&lt;/td&gt;
&lt;td&gt;$8/mo&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;$70/mo&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$10/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;*Via Custom Actions&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  My Recommendation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For most developers:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Webhook Debugger &amp;amp; Logger offers the best balance. Pay-per-event pricing means you only pay for what you use, and features like replay, mocking, and schema validation make serious debugging possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For local development:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ngrok is still the go-to when you need localhost exposure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For quick tests:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Webhook.site or RequestBin work fine for simple inspections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For enterprise:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Hookdeck provides production reliability at enterprise pricing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I personally saved 10+ hours per week switching to Webhook Debugger for Stripe and GitHub integrations. The ability to replay requests and validate schemas automatically has been a game-changer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What webhooks are you debugging?&lt;/strong&gt; Drop a comment! 💬&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
