<?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>Forem: Hookdeck</title>
    <description>The latest articles on Forem by Hookdeck (@hookdeck).</description>
    <link>https://forem.com/hookdeck</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%2Forganization%2Fprofile_image%2F3754%2F38a705a1-d1ca-44b3-8d42-d806dcaa7dc5.png</url>
      <title>Forem: Hookdeck</title>
      <link>https://forem.com/hookdeck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hookdeck"/>
    <language>en</language>
    <item>
      <title>How to Make Your AI Agent Get Webhooks Right (A Guide to Webhook Skills)</title>
      <dc:creator>Phil Leggetter</dc:creator>
      <pubDate>Wed, 18 Feb 2026 15:27:46 +0000</pubDate>
      <link>https://forem.com/hookdeck/how-to-make-your-ai-agent-get-webhooks-right-a-guide-to-webhook-skills-17im</link>
      <guid>https://forem.com/hookdeck/how-to-make-your-ai-agent-get-webhooks-right-a-guide-to-webhook-skills-17im</guid>
      <description>&lt;p&gt;When I ask my AI coding agent to set up webhooks from a new API (Stripe, Shopify, GitHub, whatever), the code it generates often looks fine until I run it. Then I hit signature verification failures, wrong raw body handling, or idempotency bugs that process the same event twice. Sound familiar?&lt;/p&gt;

&lt;p&gt;I created &lt;a href="https://github.com/hookdeck/webhook-skills" rel="noopener noreferrer"&gt;&lt;strong&gt;webhook-skills&lt;/strong&gt;&lt;/a&gt; to fix that. They're agent skills for webhooks: an open-source collection of provider- and framework-specific instructions and runnable examples that AI coding agents can load so they implement webhooks correctly the first time. In this guide I'll walk through how to use them, what's in them, and how you can contribute or request skills that are missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Agents Get Webhooks Wrong
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://hookdeck.com/" rel="noopener noreferrer"&gt;Hookdeck&lt;/a&gt; we process billions of webhooks a week, and we've seen every failure mode: signature mismatches from body parsing middleware, framework-specific gotchas that only show up in production, and so on. AI agents struggle with the same things. Their training data goes stale quickly. API versions change, security practices evolve, and the details that matter (raw body handling, middleware order, encoding) aren't in the model.&lt;/p&gt;

&lt;p&gt;Research from PostHog on &lt;a href="https://posthog.com/blog/correct-llm-code-generation" rel="noopener noreferrer"&gt;LLM code generation&lt;/a&gt; backs this up: the most reliable way to get correct code isn't the model's general knowledge; it's giving it specific, known-working examples to reference. Webhook skills are built to fill that gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Webhook Skills Fix It
&lt;/h2&gt;

&lt;p&gt;Webhook-skills is built on the &lt;a href="https://github.com/agent-skills/spec" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt;, an open standard for packaging knowledge that agents can consume. In practice that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runnable examples:&lt;/strong&gt; complete, minimal apps the agent can reference and adapt, not just snippets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider-specific guidance:&lt;/strong&gt; Stripe's raw body requirement, Shopify's HMAC encoding, and other gotchas we see trip up developers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework-aware implementations:&lt;/strong&gt; how Next.js, Express, and FastAPI handle request bodies, middleware order, and async patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staged workflows:&lt;/strong&gt; verify signature first, parse payload second, handle idempotently third.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I ask my agent to "add Stripe webhooks to my Next.js app," it doesn't hallucinate a generic handler. It has the exact patterns for App Router body handling, preserving the raw body before verification, and pulling the webhook secret from env with the right naming. That's the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install the skills
&lt;/h3&gt;

&lt;p&gt;I use &lt;code&gt;npx skills&lt;/code&gt; to add webhook-skills to my project. First I list what's available, then I install the skills I need:&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;# List available skills&lt;/span&gt;
npx skills add hookdeck/webhook-skills &lt;span class="nt"&gt;--list&lt;/span&gt;

&lt;span class="c"&gt;# Install best-practice patterns (verify → parse → handle idempotently)&lt;/span&gt;
npx skills add hookdeck/webhook-skills &lt;span class="nt"&gt;--skill&lt;/span&gt; webhook-handler-patterns

&lt;span class="c"&gt;# Install specific provider skills&lt;/span&gt;
npx skills add hookdeck/webhook-skills &lt;span class="nt"&gt;--skill&lt;/span&gt; stripe-webhooks
npx skills add hookdeck/webhook-skills &lt;span class="nt"&gt;--skill&lt;/span&gt; shopify-webhooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I usually install &lt;code&gt;webhook-handler-patterns&lt;/code&gt; plus the provider skill for whichever API I'm integrating. That gives my agent both the general flow and the provider-specific details.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to prompt
&lt;/h3&gt;

&lt;p&gt;Once the skills are installed, I prompt naturally. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Add Shopify webhook handling to my Express app."&lt;/li&gt;
&lt;li&gt;"Set up Stripe webhooks in my Next.js app."&lt;/li&gt;
&lt;li&gt;"Implement GitHub webhook verification in my FastAPI service."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent has the patterns; I don't need to spell out raw body or signature verification. It already knows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local testing (optional)
&lt;/h3&gt;

&lt;p&gt;When I'm testing webhooks locally, I use the &lt;a href="https://github.com/hookdeck/hookdeck-cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt;. It gives me a public URL that tunnels to my local server and a UI to inspect and replay requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; hookdeck-cli
&lt;span class="c"&gt;# or: brew install hookdeck/hookdeck/hookdeck&lt;/span&gt;

hookdeck listen 3000 &lt;span class="nt"&gt;--path&lt;/span&gt; /webhooks/stripe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No account required to get started. The skills work with or without Hookdeck; they're just complementary when you want to receive real webhooks on localhost.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Available (and What's Missing)
&lt;/h2&gt;

&lt;p&gt;Right now webhook-skills covers the providers and frameworks we see most often:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Providers:&lt;/strong&gt; &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks" rel="noopener noreferrer"&gt;Shopify&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/resend-webhooks" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/paddle-webhooks" rel="noopener noreferrer"&gt;Paddle&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/elevenlabs-webhooks" rel="noopener noreferrer"&gt;ElevenLabs&lt;/a&gt;, &lt;a href="https://github.com/hookdeck/webhook-skills/tree/main/skills/chargebee-webhooks" rel="noopener noreferrer"&gt;Chargebee&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frameworks:&lt;/strong&gt; Next.js, Express, and FastAPI.&lt;/p&gt;

&lt;p&gt;Each provider skill includes signature verification, event handling guidance, common failure modes, and testing tips for local dev. Coverage isn't complete yet. If you need a provider we don't have or you want to contribute, see the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Contribute and Ask for Skills
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Request a skill:&lt;/strong&gt; If you're integrating a provider we don't support yet, &lt;a href="https://github.com/hookdeck/webhook-skills/issues" rel="noopener noreferrer"&gt;open an issue on GitHub&lt;/a&gt; with a title like &lt;strong&gt;"Skill request: [Provider] webhooks"&lt;/strong&gt;. Describe the provider and your framework if relevant. I use these to prioritize what to add next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contribute a skill:&lt;/strong&gt; If you've built a webhook integration you're proud of, PRs for new providers or frameworks are welcome. The repo is &lt;a href="https://github.com/hookdeck/webhook-skills" rel="noopener noreferrer"&gt;hookdeck/webhook-skills&lt;/a&gt;. Check the existing skills for structure and open a PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API and platform maintainers:&lt;/strong&gt; If you maintain a webhook-producing API and want AI coding agents to implement your webhooks correctly out of the box, I'd love a skill from you. Open an issue or PR and we can align on format and content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give It a Shot
&lt;/h2&gt;

&lt;p&gt;If you've ever lost an afternoon to webhook signature verification or body parsing, try webhook-skills the next time you're wiring up an integration. Install the skills, prompt your agent, and you might find it finally does what you meant.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hookdeck/webhook-skills" rel="noopener noreferrer"&gt;webhook-skills on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hookdeck/hookdeck-cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; (optional, for local webhook testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have feedback or want to request or contribute a skill? &lt;a href="https://github.com/hookdeck/webhook-skills/issues" rel="noopener noreferrer"&gt;Open an issue&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Introducing The Event Destinations Initiative</title>
      <dc:creator>Phil Leggetter</dc:creator>
      <pubDate>Thu, 27 Feb 2025 13:57:21 +0000</pubDate>
      <link>https://forem.com/hookdeck/introducing-the-event-destinations-initiative-4065</link>
      <guid>https://forem.com/hookdeck/introducing-the-event-destinations-initiative-4065</guid>
      <description>&lt;p&gt;The Event Destinations Initiative is a community effort aimed at creating a model for event interoperability between event producers and their consumers. This initiative focuses on providing a set of guidelines for managing event destinations, ensuring seamless integration, improved reliability, greater event delivery choice, and a much-improved developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Event Destinations?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hookdeck.com/blog/event-destinations" rel="noopener noreferrer"&gt;Event Destinations&lt;/a&gt; are the endpoints where API platform events are delivered and consumed. Event destinations can include webhook endpoints, message queues, APIs, &lt;a href="https://hookdeck.com/blog/event-destinations/blog/event-gateway-definition" rel="noopener noreferrer"&gt;event gateways&lt;/a&gt;, and other services that handle event data. Webhooks are the most ubiquitous form of event destination, but the Event Destinations Initiative aims to establish a set of guidelines for API platforms to provide additional event delivery options offering more choice, improved reliability at scale, and an overall better developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Event Destinations Initiative?
&lt;/h2&gt;

&lt;p&gt;The Event Destinations Initiative is a collaborative project that aims to create a set of guidelines for defining, managing, and utilizing event destinations across platforms and services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standardization&lt;/strong&gt;: Establishing a common set of guidelines and best practices for event destinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interoperability&lt;/strong&gt;: Ensuring compatibility across different platforms and services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Providing solutions that can scale with the growing demands of modern applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Enhancing the reliability and fault tolerance of event-driven architectures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Integration&lt;/strong&gt;: Easier integration with commonly used services and platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Efficiency&lt;/strong&gt;: Streamlined event publishing, routing, and ingestion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Reliability&lt;/strong&gt;: More robust and fault-tolerant event handling for both the publisher and consumer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-Proofing&lt;/strong&gt;: A forward-looking approach that anticipates future needs and challenges.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Supporters are Saying
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In his book Flow Architectures, James Urquhart envisions a future where APIs are inherently asynchronous, with synchronous APIs being reserved only for scenarios where they are truly necessary.&lt;/p&gt;

&lt;p&gt;The Event Destinations Initiative represents a significant step toward realizing that vision. Enabling API producers to adopt the most efficient and performant methods for sending and receiving asynchronous, non-blocking data is a development I wholeheartedly support—particularly when it is driven by a collaborative and open effort.&lt;/p&gt;

&lt;p&gt;This direction strongly aligns with the vision we champion at AsyncAPI, and the synergy between these efforts is both promising and exciting. I am eager to see the Event Destinations Initiative succeed and contribute to the evolution and blending of API and EDA ecosystems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;-- &lt;strong&gt;Fran Méndez, Founder @ AsyncAPI Initiative&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've been working with (and often against) webhooks for years, both as a consumer and an implementer, always wishing that we as an industry could come up with something better. In my opinion, Event Destinations are exactly that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;-- &lt;strong&gt;Paul Asjes, Developer Experience Engineer @ ElevenLabs&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Involved
&lt;/h2&gt;

&lt;p&gt;We invite developers, architects, and organizations to join us in this initiative. By collaborating, we can build a more efficient and reliable event-driven ecosystem.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://eventdestinations.org/" rel="noopener noreferrer"&gt;eventdestinations.org&lt;/a&gt; to learn more and get involved.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>eventdriven</category>
      <category>pubsub</category>
      <category>api</category>
    </item>
    <item>
      <title>Free ngrok alternative for async web dev - the Hookdeck CLI</title>
      <dc:creator>Phil Leggetter</dc:creator>
      <pubDate>Mon, 29 Jul 2024 18:31:55 +0000</pubDate>
      <link>https://forem.com/hookdeck/free-ngrok-alternative-for-async-web-dev-the-hookdeck-cli-5f6j</link>
      <guid>https://forem.com/hookdeck/free-ngrok-alternative-for-async-web-dev-the-hookdeck-cli-5f6j</guid>
      <description>&lt;p&gt;ngrok is great. It's been the go-to for many a web developer who wants to be able to expose a locally running web application to the public Internet for many years. And it's still a great solution. However, ngrok has changed in ways that have resulted in a much poorer developer experience and there are now better tools for some of the jobs that ngrok was originally the go-to for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's changed with ngrok?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ngrok used to be free and only required an account for &lt;a href="https://github.com/inconshreveable/ngrok/blob/64ebca99eb355cae5a5498c76f8a318a782a0442/README.md#downloading-and-installing-ngrok" rel="noopener noreferrer"&gt;"premium features"&lt;/a&gt;. Now, you need an ngrok account no matter your use case.&lt;/li&gt;
&lt;li&gt;There's a 1GB limit a month on data transfer (in all fairness, this should be more than enough for development)&lt;/li&gt;
&lt;li&gt;The free ephemeral/random domains (the domain name changes every time you restart ngrok) have always been a bit of a pain. The first paid plan used to come with 3 subdomains but now only comes with one, making exposing multiple services at the same time more costly.&lt;/li&gt;
&lt;/ul&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%2Ffud9jg4ebgo97gw6j4rv.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%2Ffud9jg4ebgo97gw6j4rv.png" alt="ngrok pricing, April 2021" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhalazvjej0woz2yi14t8.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%2Fhalazvjej0woz2yi14t8.png" alt="ngrok pricing, July 2024" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More broadly, Their product focus has shifted reasonably dramatically from supporting use cases such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Temporarily sharing a website that is only running on your development machine&lt;/li&gt;
&lt;li&gt;Demoing an app at a hackathon without deploying&lt;/li&gt;
&lt;li&gt;Developing any services that consume webhooks (HTTP callbacks) by allowing you to replay those requests&lt;/li&gt;
&lt;li&gt;Debugging and understanding any web service by inspecting the HTTP traffic&lt;/li&gt;
&lt;li&gt;Running networked services on machines that are firewalled off from the internet&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;To:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All-in-one API gateway, Kubernetes Ingress, DDoS protection, firewall, and global load balancing as a service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, ngrok has shifted focus away from supporting development use cases. Therefore, it's likely time to try an alternative tool that has been built to specifically support your use case.&lt;/p&gt;

&lt;p&gt;The rest of this post will show how the Hookdeck CLI can be used to receive asynchronous events (a.k.a. webhooks) from third-party services such as Stripe, Shopify, or Twilio. If you're looking for a tool that solves the other use cases, take a look at the &lt;a href="https://github.com/anderspitman/awesome-tunneling" rel="noopener noreferrer"&gt;awesome tunneling repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is synchronous web development?
&lt;/h2&gt;

&lt;p&gt;As per the title of this post, &lt;a href="https://github.com/hookdeck/hookdeck-cli" rel="noopener noreferrer"&gt;the Hookdeck CLI&lt;/a&gt; is built to support asynchronous web development. By this, I mean requests, such as webhooks from an API service, are received and proxied onto the application running locally. However, the response from the local service is not passed back to the API service that made the request.&lt;/p&gt;

&lt;p&gt;The Hookdeck CLI and the &lt;a href="https://hookdeck.com?ref-dev-to-cli" rel="noopener noreferrer"&gt;Hookdeck Event Gateway&lt;/a&gt; are built to support asynchronous messaging with event-driven applications and not the request/response paradigm. Therefore, you can't use the Hookdeck CLI to expose and serve a locally running website or request/response API. If you want to send the result of some work to another service, you should make a separate request to that service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Hookdeck CLI as a free alternative to ngrok for webhooks
&lt;/h2&gt;

&lt;p&gt;The next section walks through installing the Hookdeck CLI, creating and running a local web server (which you can skip if you already have this running), creating a localtunnel using the CLI, sending test webhook events, and replaying webhooks using the Hookdeck Console.&lt;/p&gt;

&lt;p&gt;This guide will use the Node.js runtime. If you prefer to use Python, you can run through a &lt;a href="https://guides.curiousmints.com/hello-world-guides/hookdeck-localhost-webhooks" rel="noopener noreferrer"&gt;using the Hookdeck CLI with a Python server to receive webhooks&lt;/a&gt; guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the Hookdeck CLI
&lt;/h3&gt;

&lt;p&gt;Via NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i hookdeck-cli &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Mac:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck/hookdeck/hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also &lt;a href="https://github.com/hookdeck/hookdeck-cli?tab=readme-ov-file#linux-or-without-package-managers" rel="noopener noreferrer"&gt;instructions for installing on Linux&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create and run a localhost Node.js web server
&lt;/h3&gt;

&lt;p&gt;Open your terminal and navigate to a directory for your project.&lt;/p&gt;

&lt;p&gt;Install &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt; to use as the web server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save&lt;/span&gt; express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;server.js&lt;/code&gt; file in the project root:&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;express&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="s1"&gt;express&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3030&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="na"&gt;webhook_received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCEPTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="s2"&gt;`🪝 Server running at http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use the Hookdeck CLI to create a localtunnel
&lt;/h3&gt;

&lt;p&gt;Run the following command in a new terminal window to create a localtunnel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 3030 my-webhook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Dashboard
👤 Console URL: https://api.hookdeck.com/signin/guest?token&lt;span class="o"&gt;={&lt;/span&gt;token&lt;span class="o"&gt;}&lt;/span&gt;
Sign up &lt;span class="k"&gt;in &lt;/span&gt;the Console to make your webhook URL permanent.

Sources
🔌 my-webhook URL: https://hkdk.events/wggd60fl5cxw20

Connections
my-webhook -&amp;gt; my-webhook-to-cli forwarding to /

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Ready! &lt;span class="o"&gt;(&lt;/span&gt;^C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test receiving a webhook with a cURL command
&lt;/h3&gt;

&lt;p&gt;Run the following cURL command to act as an inbound webhook, replacing the URL with the &lt;strong&gt;Event URL&lt;/strong&gt; from the Hookdeck CLI output:&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://hkdk.events/cnzpo680rdhejk &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;'{"message": "Hello, World!"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cURL command output will be similar to the following:&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="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;:&lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt;,&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"Request handled by Hookdeck. Check your dashboard to inspect the request: https://dashboard.hookdeck.com/requests/req_[id]"&lt;/span&gt;,&lt;span class="s2"&gt;"request_id"&lt;/span&gt;:&lt;span class="s2"&gt;"req_[id]"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the terminal running the Hookdeck CLI log the inbound webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;2024-07-09 19:06:46 &lt;span class="o"&gt;[&lt;/span&gt;200] POST http://localhost:3030/ | https://console.hookdeck.com/?event_id&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will also see the Python server log the inbound webhook:&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="o"&gt;{&lt;/span&gt;
  webhook_received: &lt;span class="s1"&gt;'2024-07-25T10:08:22.945Z'&lt;/span&gt;,
  body: &lt;span class="o"&gt;{&lt;/span&gt; message: &lt;span class="s1"&gt;'Hello, World!'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trigger a test webhook using the Hookdeck Console
&lt;/h3&gt;

&lt;p&gt;Open the &lt;strong&gt;Console URL&lt;/strong&gt; from your terminal in your browser.&lt;/p&gt;

&lt;p&gt;Choose a &lt;strong&gt;Sample Webhook Provider&lt;/strong&gt; from the list of &lt;strong&gt;Example Webhooks&lt;/strong&gt;. For example, &lt;strong&gt;Stripe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Select a &lt;strong&gt;Sample Webhook Type&lt;/strong&gt; from the list on the right. For example, &lt;strong&gt;invoice.created&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Send&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Hookdeck Console will show the test webhook has been triggered. You can also inspect the webhook payload and the localhost web server response.&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%2F90sb9hvdumpq06l19jmh.gif" 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%2F90sb9hvdumpq06l19jmh.gif" alt="Trigger test webhook, inspect webhook payload, and view the localhost server response" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will see the terminal running the Hookdeck CLI log the inbound webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;2024-07-09 19:06:46 &lt;span class="o"&gt;[&lt;/span&gt;200] POST http://localhost:3030/webhook | https://console.hookdeck.com/?event_id&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will also see the Python server log the inbound webhook:&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="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"evt_1MhUT7E0b6fckueStwy86nfu"&lt;/span&gt;,
  &lt;span class="s2"&gt;"object"&lt;/span&gt;: &lt;span class="s2"&gt;"event"&lt;/span&gt;,
  &lt;span class="s2"&gt;"api_version"&lt;/span&gt;: &lt;span class="s2"&gt;"2022-11-15"&lt;/span&gt;,
  &lt;span class="s2"&gt;"created"&lt;/span&gt;: 1677833999,
  &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    ...
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"invoice.created"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Replay a webhook
&lt;/h3&gt;

&lt;p&gt;To replay the webhook, go to the Hookdeck Console and click on the &lt;strong&gt;Resend to destination&lt;/strong&gt; button.&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%2F8pmsdfbpzdddz8ne476b.gif" 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%2F8pmsdfbpzdddz8ne476b.gif" alt="Replay Webhook" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Receive a webhook from an API platform
&lt;/h3&gt;

&lt;p&gt;Copy the &lt;strong&gt;Event URL&lt;/strong&gt; from the Hookdeck CLI output. You can also find the same URL in the Hookdeck Console.&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%2F5uiznlvquzfxtwo2p47v.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%2F5uiznlvquzfxtwo2p47v.png" alt="Copy Hookdeck Event URL" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to the API provider platform, such as Resend, Twilio, Shopify, or Stripe, and register the Hookdeck URL as the webhook URL with the provider.&lt;/p&gt;

&lt;p&gt;Trigger a webhook from your selected API provider to see a log entry in the Hookdeck Console.&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%2F2oy8hjns1zcvquhlao7s.gif" 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%2F2oy8hjns1zcvquhlao7s.gif" alt="Inbound webhook from Vonage SMS API handled in the Hookdeck Console" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will also see the webhook logged in the Hookdeck CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;2024-07-09 19:45:57 &lt;span class="o"&gt;[&lt;/span&gt;200] POST http://localhost:3030/webhook | https://console.hookdeck.com/?event_id&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And by the Node.js server running in your local development environment:&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="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"to"&lt;/span&gt;: &lt;span class="s2"&gt;"{to_number}"&lt;/span&gt;,
  &lt;span class="s2"&gt;"from"&lt;/span&gt;: &lt;span class="s2"&gt;"{from_number}"&lt;/span&gt;,
  &lt;span class="s2"&gt;"channel"&lt;/span&gt;: &lt;span class="s2"&gt;"sms"&lt;/span&gt;,
  &lt;span class="s2"&gt;"message_uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"461c9502-3c2f-4af9-8862-c5a8eccf6cfe"&lt;/span&gt;,
  &lt;span class="s2"&gt;"timestamp"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-07-09T18:45:56Z"&lt;/span&gt;,
  &lt;span class="s2"&gt;"usage"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"price"&lt;/span&gt;: &lt;span class="s2"&gt;"0.0057"&lt;/span&gt;,
    &lt;span class="s2"&gt;"currency"&lt;/span&gt;: &lt;span class="s2"&gt;"EUR"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"message_type"&lt;/span&gt;: &lt;span class="s2"&gt;"text"&lt;/span&gt;,
  &lt;span class="s2"&gt;"text"&lt;/span&gt;: &lt;span class="s2"&gt;"Inbound SMS from the Vonage API"&lt;/span&gt;,
  &lt;span class="s2"&gt;"context_status"&lt;/span&gt;: &lt;span class="s2"&gt;"none"&lt;/span&gt;,
  &lt;span class="s2"&gt;"origin"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"network_code"&lt;/span&gt;: &lt;span class="s2"&gt;"23415"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"sms"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"num_messages"&lt;/span&gt;: &lt;span class="s2"&gt;"1"&lt;/span&gt;,
    &lt;span class="s2"&gt;"count_total"&lt;/span&gt;: &lt;span class="s2"&gt;"1"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;p&gt;You have successfully received a webhook on your local development environment using the Hookdeck CLI. You also inspected the webhook payload and server response, and replayed a webhook using the Hookdeck Console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more about Hookdeck
&lt;/h2&gt;

&lt;p&gt;This guide demonstrated how to use the &lt;a href="https://github.com/hookdeck/hookdeck-cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; as a replacement for ngrok for local asynchronous web development. The Hookdeck CLI is free to use, and you don't need a Hookdeck account.&lt;/p&gt;

&lt;p&gt;If you're interested in finding out more about Hookdeck including features such as &lt;a href="https://hookdeck.com/docs/transformations?ref=dev-to-cli" rel="noopener noreferrer"&gt;transformations&lt;/a&gt;, and &lt;a href="https://hookdeck.com/docs/filters?ref=dev-to-cli" rel="noopener noreferrer"&gt;filtering&lt;/a&gt;, benefitting from functionality like &lt;a href="https://hookdeck.com/docs/retries?ref=dev-to-cli" rel="noopener noreferrer"&gt;configurable retries&lt;/a&gt;, and generally using Hookdeck as your reliable inbound webhook infrastructure, head to &lt;a href="https://hookdeck.com?ref=dev-to-cli" rel="noopener noreferrer"&gt;hookdeck.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ngrok</category>
      <category>webhooks</category>
      <category>cli</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>Add Webhook Verification, Queueing, Filtering, and Retry Logic to Any Vercel Deployed Endpoint</title>
      <dc:creator>Phil Leggetter</dc:creator>
      <pubDate>Mon, 06 May 2024 18:38:18 +0000</pubDate>
      <link>https://forem.com/hookdeck/add-webhook-verification-queueing-filtering-and-retry-logic-to-any-vercel-deployed-endpoint-4bnp</link>
      <guid>https://forem.com/hookdeck/add-webhook-verification-queueing-filtering-and-retry-logic-to-any-vercel-deployed-endpoint-4bnp</guid>
      <description>&lt;p&gt;Today we're excited to announce the Hookdeck Vercel Middleware. Vercel middleware that convert any Vercel application endpoint into an asynchronous endpoint with authentication, filters, queueing, throttling, and retry logic.&lt;/p&gt;

&lt;p&gt;Every month, we deliver hundreds of thousands of asynchronous events to Vercel application endpoints, so we want to make debugging and building with webhooks and other asynchronous messaging use cases on Vercel even easier.&lt;/p&gt;

&lt;p&gt;The Hookdeck Vercel Middleware requires minimal effort (install + simple configuration) and a DX that fits into a code-first workflow. So, within a few minutes, you've converted a Next.js route, for example, into a reliable and feature-rich asynchronous endpoint.&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%2Flm13fklf1jqk6n6wirsx.jpeg" 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%2Flm13fklf1jqk6n6wirsx.jpeg" alt="Hookdeck Vercel Middleware configuration" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Along with the simple setup, I believe this solves a real problem with numerous use cases. The main one is handling webhooks (Stripe, Shopify, Twilio, etc.) at a scale where you really don't want to miss a webhook.&lt;/p&gt;

&lt;p&gt;Here's a 5-minute video of our CEO and co-founder, Alex, installing, configuring, and deploying a Next.js app: &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/bQzyhyVNbY8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can see the quickstart instructions (along with the middleware source) in the &lt;a href="https://github.com/hookdeck/hookdeck-vercel" rel="noopener noreferrer"&gt;Hookdeck Vercel Middleware GitHub repo&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://hookdeck-vercel-example.vercel.app/" rel="noopener noreferrer"&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%2F2w9nnrnnm0j9d0p5ry6k.png" alt="Hookdeck Vercel demo app" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also &lt;a href="https://github.com/hookdeck/hookdeck-vercel-example" rel="noopener noreferrer"&gt;example Next.js app&lt;/a&gt; with a &lt;a href="https://hookdeck-vercel-example.vercel.app/" rel="noopener noreferrer"&gt;deployed demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let us know what you think.&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>javascript</category>
      <category>webhooks</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Shopify Webhooks Tutorial: How to Use the Shopify API</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:46:54 +0000</pubDate>
      <link>https://forem.com/hookdeck/shopify-webhooks-tutorial-how-to-use-the-shopify-api-1mk6</link>
      <guid>https://forem.com/hookdeck/shopify-webhooks-tutorial-how-to-use-the-shopify-api-1mk6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Shopify webhooks help build integrations with external applications that need to respond in real-time to events taking place in a Shopify store. You can create and manage Shopify webhooks using the Shopify admin dashboard. Although easy to use, the admin interface does not allow developers to perform automated tasks on their webhooks. Instead, users have to log into the admin interface and manually perform webhook-related tasks.&lt;/p&gt;

&lt;p&gt;As a solution to this drawback, Shopify opened up its &lt;a href="https://shopify.dev/api/admin-rest/2021-10/resources/webhook" rel="noopener noreferrer"&gt;webhook API&lt;/a&gt;. This allows programmatic interaction with Shopify webhooks, thereby expanding the scope of what is possible with the technology. &lt;/p&gt;

&lt;p&gt;In this article, I will walk you through how to set up authentication with the Shopify API, and then use the Shopify webhooks API to create and view your webhooks. You will also use a locally running API to receive and log your webhooks.&lt;/p&gt;

&lt;p&gt;Let's get right into it!&lt;/p&gt;

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

&lt;p&gt;To follow along with this tutorial, you will need a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Shopify store&lt;/li&gt;
&lt;li&gt;The ability to create &lt;a href="https://help.shopify.com/en/manual/apps/private-apps" rel="noopener noreferrer"&gt;Shopify private apps&lt;/a&gt; (you may not have this permission if you're not the direct owner of the store you're working with)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your system (if you already have a local API that you prefer to use, that is fine as long as you make sure it exposes a &lt;code&gt;POST&lt;/code&gt; endpoint and listens on a known port)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; installed (you can find details on installing the tool &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Shopify store app
&lt;/h2&gt;

&lt;p&gt;The first step to accessing and using the Shopify webhooks API is to create a Shopify store app. There are 3 types of Shopify apps: public, custom, and private. To learn more about the different types of Shopify apps and how to create them, read the documentation pages &lt;a href="https://help.shopify.com/en/manual/apps/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To access the Shopify API, we are going to create a private app. Private apps are built to work exclusively for your Shopify store (unlike public apps that can be used by any Shopify store and are listed on the &lt;a href="https://apps.shopify.com/" rel="noopener noreferrer"&gt;Shopify App store&lt;/a&gt;). Private apps also allow you to access your store's data directly using the Shopify API.&lt;/p&gt;

&lt;p&gt;To create a Shopify app, click &lt;code&gt;Apps&lt;/code&gt; on the side menu of the admin interface. Just below the main section on the Apps page, click the &lt;code&gt;Manage Private Apps&lt;/code&gt; link and then click the &lt;code&gt;Create private app&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;On the app creation page, enter the name for the application you are about to create (in this example, &lt;code&gt;Purchase Log API&lt;/code&gt;) and enter your email. Next, in the &lt;code&gt;Admin API&lt;/code&gt; section, expand the &lt;code&gt;Show Inactive Admin API&lt;/code&gt; and give &lt;code&gt;Read and write&lt;/code&gt; access to the following resources...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order editing&lt;/li&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...as shown below:&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%2Fefe0ac3biahvbaqko81l.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%2Fefe0ac3biahvbaqko81l.png" alt="Order permissions" width="734" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the permissions are set, scroll down and select the latest API version (at the time of this writing, the latest version is &lt;strong&gt;2021-10&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; to complete this process. You will be prompted with a pre-save message informing you that an API key will be generated to provide access to the store's data.&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%2Fmokflpjkcb7glw7sv2u2.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%2Fmokflpjkcb7glw7sv2u2.png" alt="Pre-save message" width="787" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Create app&lt;/code&gt; on this prompt and you will be taken to the app page. On this page, you will find the following credentials in the &lt;code&gt;Admin API&lt;/code&gt; section:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API key&lt;/strong&gt; (this is your username when using &lt;a href="https://shopify.dev/apps/auth/basic-http" rel="noopener noreferrer"&gt;Basic authentication&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password&lt;/strong&gt; (this is your API access token)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example URL&lt;/strong&gt; (a sample URL format for performing basic authentication using a username and password)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Secret&lt;/strong&gt; (used for verifying webhooks, more on this later)&lt;/li&gt;
&lt;/ul&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%2Ffb2xfgzqx773m542jfaa.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%2Ffb2xfgzqx773m542jfaa.png" alt="Admin API masked" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these credentials, you have all you need to authenticate with the Shopify API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticating with the Shopify API
&lt;/h2&gt;

&lt;p&gt;Now that you have your authentication credentials, let's discuss how you will go about making authenticated calls to the Shopify API. It is important to note that the different types of Shopify apps do not use the same authentication strategy.&lt;/p&gt;

&lt;p&gt;While public and custom apps use &lt;a href="https://shopify.dev/apps/auth/oauth" rel="noopener noreferrer"&gt;OAuth&lt;/a&gt; and &lt;a href="https://shopify.dev/apps/auth/session-tokens" rel="noopener noreferrer"&gt;session tokens&lt;/a&gt;, private apps use &lt;a href="https://shopify.dev/apps/auth/basic-http" rel="noopener noreferrer"&gt;basic authentication&lt;/a&gt;. In this section, I will only be discussing authentication for private apps.&lt;/p&gt;

&lt;p&gt;There are 3 ways you can authenticate with the Shopify API using credentials from your private app.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Username and password combo
&lt;/h3&gt;

&lt;p&gt;If your client supports basic authentication, you can include authentication credentials in your URL. This is done by prepending &lt;code&gt;username:password@&lt;/code&gt; to the hostname in the URL as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;password&lt;span class="o"&gt;}&lt;/span&gt;@&lt;span class="o"&gt;{&lt;/span&gt;shop&lt;span class="o"&gt;}&lt;/span&gt;.myshopify.com/admin/api/&lt;span class="o"&gt;{&lt;/span&gt;version&lt;span class="o"&gt;}&lt;/span&gt;/webhooks.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt;: Your API key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt;: Your private app password.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shop&lt;/code&gt;: Your Shopify store subdomain string, e.g. &lt;code&gt;hookdeck-store&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: The version of the API you're using, e.g. &lt;code&gt;2021-10&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any request made to the Shopify API using this URL is automatically authenticated.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Using an authorization token
&lt;/h3&gt;

&lt;p&gt;Some clients do not support basic authentication as used in the above method. You can go around this issue by using the &lt;code&gt;Basic {token}&lt;/code&gt; value in the &lt;code&gt;Authorization&lt;/code&gt; header. The &lt;code&gt;token&lt;/code&gt; value is derived by joining your store app's API key and password with a colon (&lt;code&gt;:&lt;/code&gt;) and encoding the resulting string in base64. The &lt;code&gt;Authorization&lt;/code&gt; header can then be sent, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Authorization: Basic &lt;span class="nv"&gt;NDQ3OGViN2FjMTM4YTEzNjg1MmJhYmQ4NjE5NTZjMTk6M2U1YTZlZGVjNzFlYWIwMzk0MjJjNjQ0NGQwMjY1OWQ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3) Using the X-Shopify-Access-Token header
&lt;/h3&gt;

&lt;p&gt;The last strategy for authenticating private apps is to send the &lt;code&gt;X-Shopify-Access-Token&lt;/code&gt; header, along with your request, to the Shopify API. The value of this header is your private app's password, which is your access token. The header is sent as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;X-Shopify-Access-Token: &lt;span class="o"&gt;{&lt;/span&gt;my_app_password&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the strategy we will be using for all our API calls in this tutorial. &lt;/p&gt;

&lt;p&gt;Now that you know how to authenticate with the Shopify API, let's start creating and receiving Shopify webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up and receiving Shopify webhooks using the Shopify API
&lt;/h2&gt;

&lt;p&gt;As described earlier, we will create a webhook using the Shopify API. Then, we'll spin up a local API to receive and log part of the webhook payload.&lt;/p&gt;

&lt;p&gt;Before we begin, let's walk you through the steps necessary to achieve this webhook logging setup.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the demo API and run it locally&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; to generate a webhook URL that points to the API endpoint&lt;/li&gt;
&lt;li&gt;Create a webhook using the API&lt;/li&gt;
&lt;li&gt;Test your webhook by triggering the subscription event&lt;/li&gt;
&lt;li&gt;Inspect your webhook and webhook logs&lt;/li&gt;
&lt;li&gt;Verify the webhook payload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that you're caught up, let's begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone the demo API
&lt;/h3&gt;

&lt;p&gt;Clone the application repository for the API by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; shopify-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;cd &lt;/span&gt;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, you can then run the Node.js server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-shopify-webhook&lt;/code&gt;: This is the endpoint that will be receiving the Shopify webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generate the webhook URL using the Hookdeck CLI
&lt;/h3&gt;

&lt;p&gt;The next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port &lt;code&gt;1337&lt;/code&gt; value with the port number on which your API is running if you're not using the sample project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;em&gt;**Create new source&lt;/em&gt;* (this prompt does not show up if you have not created any previous connections)*&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;Shopify&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-shopify-webhook&lt;/em&gt;&lt;/strong&gt; (if you're using your own custom server, replace this value with your endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;Purchase Log API&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6d45e136-4e04-4300-a3b1-49a817aaf24f%2Fcli-ready.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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6d45e136-4e04-4300-a3b1-49a817aaf24f%2Fcli-ready.png" alt="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6d45e136-4e04-4300-a3b1-49a817aaf24f/cli-ready.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're running the CLI in guest mode, you will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Shopify webhook using the API
&lt;/h3&gt;

&lt;p&gt;Now that you have your webhook URL, let's create the webhook. Shopify's webhook API can be accessed at &lt;code&gt;https://{shop}.myshopify.com/admin/{version}/webhooks.json&lt;/code&gt; (remember to replace the values in &lt;code&gt;{}&lt;/code&gt; with yours).&lt;/p&gt;

&lt;p&gt;To create a webhook, you need to send a &lt;code&gt;POST&lt;/code&gt; request to this endpoint containing data. You can use any HTTP client of your choice (&lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;, &lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;Curl&lt;/a&gt;, etc.). In this tutorial, I am using &lt;a href="https://reqbin.com" rel="noopener noreferrer"&gt;ReqBin&lt;/a&gt; because it is accessible and easy to use.&lt;/p&gt;

&lt;p&gt;For the webhook you are creating in this tutorial, you must subscribe to the &lt;code&gt;orders/create&lt;/code&gt;. This event is fired when a new order is created in your store.&lt;/p&gt;

&lt;p&gt;The data to send along with your webhook creation request should be structured as follows:&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;"webhook"&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="nl"&gt;"topic"&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="s2"&gt;"orders/create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{webhook_url}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&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;As seen in the above snippet, the format to receive the webhook is set as &lt;code&gt;json&lt;/code&gt;, so remember to replace &lt;code&gt;{webhook_url}&lt;/code&gt; with the webhook URL generated on the Hookdeck CLI session.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://reqbin.com" rel="noopener noreferrer"&gt;https://reqbin.com&lt;/a&gt; and paste the API URL in the address field, then set the request method to &lt;code&gt;POST&lt;/code&gt;. For authentication, add the &lt;code&gt;X-Shopify-Access-Token&lt;/code&gt; field in the &lt;code&gt;Headers&lt;/code&gt; section as shown below (replace &lt;code&gt;MY_ACCESS_TOKEN&lt;/code&gt; with your Shopify app password):&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%2F5uouo7jdgyxxtlmkfh9x.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%2F5uouo7jdgyxxtlmkfh9x.png" alt="Set access token" width="662" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The header will now be reflected in the details under the &lt;code&gt;Raw&lt;/code&gt; section. Lastly, click the &lt;code&gt;Content&lt;/code&gt; section and paste in your request object. See below a screenshot of the request setup:&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%2F4a2x66btne15zskajomh.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%2F4a2x66btne15zskajomh.png" alt="Create webhook API request" width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now hit the &lt;code&gt;Send&lt;/code&gt; button to create your new webhook. You should get back a successful response, like you can see below:&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%2F4q3i34db1gkysc1xcm2o.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%2F4q3i34db1gkysc1xcm2o.png" alt="Webhook API response" width="800" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This indicates that you have successfully created a webhook for the &lt;code&gt;orders/create&lt;/code&gt; topic on your Shopify store.&lt;/p&gt;

&lt;p&gt;Other actions that can be performed on your Shopify webhooks using the API include updating a webhook, fetching a single webhook, and deleting an existing webhook. For more information on how to perform these operations, visit the &lt;a href="https://shopify.dev/api/admin-rest/2021-10/resources/webhook" rel="noopener noreferrer"&gt;Shopify webhooks API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your webhook
&lt;/h3&gt;

&lt;p&gt;With a locally running log API set up to receive webhooks, and a webhook subscription created via the API, the time has come to test your setup.&lt;/p&gt;

&lt;p&gt;To receive a webhook for the subscribed topic, you need to trigger the topic's event by creating a new order. You can create a new order easily on your Shopify admin interface. Below, I am placing an order for a Ben Simmons 76er's jersey:&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%2Fjqt02qyf72irhra8j7tg.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%2Fjqt02qyf72irhra8j7tg.png" alt="Create new Shopify order" width="745" height="883"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the order is created, Shopify will trigger a webhook for the &lt;code&gt;orders/create&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;This will be received in your Hookdeck CLI session as shown below:&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%2Fc1uzfgrpxxjhcfwzgdz6.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%2Fc1uzfgrpxxjhcfwzgdz6.png" alt="CLI success" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspect your webhook and webhook logs
&lt;/h3&gt;

&lt;p&gt;The Hookdeck CLI helps us easily inspect the details contained in a Shopify webhook by providing a URL to an event page that breaks down the webhook data. This URL is the last item on the webhook entry, as shown in the CLI output above. Copy this URL and load it in your browser, and you will see an event page where you can view details like the webhook headers in the &lt;code&gt;Headers&lt;/code&gt; section below:&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%2Fj8epofb0bzqh5vi5rsqp.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%2Fj8epofb0bzqh5vi5rsqp.png" alt="Event headers" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also have a browsable representation of the webhook payload in the &lt;code&gt;Body&lt;/code&gt; section:&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%2F6pcttbt3s9b3vpu7d9fg.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%2F6pcttbt3s9b3vpu7d9fg.png" alt="Event body" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's check to confirm that the locally running API is indeed logging the webhook payload. Still on your browser, navigate to the &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt; endpoint of the locally running API, and you will see a display similar to the one below (ignore the first object):&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%2Fqvcnzyq85gtv9jaq1qiz.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%2Fqvcnzyq85gtv9jaq1qiz.png" alt="Webhook logs" width="646" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen, you have been able to log the webhook ID, the total number of items in your order, and the time the webhook was delivered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify your Shopify webhooks
&lt;/h3&gt;

&lt;p&gt;Shopify sends you an &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header that can be used to verify that the payload contained in the webhook is from Shopify and has not been spoofed or tampered with.&lt;/p&gt;

&lt;p&gt;This header is an encrypted version of the payload and is computed using the private app's shared secret, the webhook payload, and the &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC cryptographic algorithm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To verify your webhook on your external API, you compute the same encrypted version of the webhook payload using the shared key and HMAC, then compare the result with the value sent in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header. If there is a match, then the payload is valid and it originated from Shopify. If not, then it's suspicious and should be ignored.&lt;/p&gt;

&lt;p&gt;You can find this verification logic in the &lt;code&gt;server.js&lt;/code&gt; file as a commented middleware function &lt;code&gt;validatePayload&lt;/code&gt;, shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-shopify-hmac-sha256&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;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&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;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;xx-xx-x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;.....&lt;/span&gt;

&lt;span class="cm"&gt;/* function validatePayload(req, res, next) {
    if(req.method == "POST"){
        if (!req.rawBody) {
            return next('Request body empty')
        }
        const body = req.rawBody;
        const hmacHeader = req.get(sigHeaderName);
        //Create a hash based on the parsed body
        const hash = crypto
            .createHmac(sigHashAlg, secret)
            .update(body, "utf8", "hex")
            .digest("base64");
        // Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header
        if (hash !== hmacHeader) {
            return next(`Request body digest (${hash}) did not match ${sigHeaderName} (${hmacHeader})`)
        }
    }
    return next()
}
app.use(validatePayload); */&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on security considerations for your webhooks, check out our &lt;a href="https://hookdeck.com/guides/webhooks/webhooks-security-checklist" rel="noopener noreferrer"&gt;security checklist&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In this article, you have learned and demonstrated how to perform Shopify webhook operations using the webhook API. Being a programmatic interface, the Shopify API allows you to run automated tasks that can, for example, update and fetch webhooks on the fly. You can perform these tasks directly in your code or within CI/CD pipelines to tailor Shopify webhooks towards your application/infrastructure needs.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webhook</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
    <item>
      <title>Tutorial: How to Use Shopify's Admin Dashboard to Create Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:30:46 +0000</pubDate>
      <link>https://forem.com/hookdeck/tutorial-how-to-use-shopifys-admin-dashboard-to-create-webhooks-4pd9</link>
      <guid>https://forem.com/hookdeck/tutorial-how-to-use-shopifys-admin-dashboard-to-create-webhooks-4pd9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a previous article, we looked at how &lt;a href="https://shopify.com/" rel="noopener noreferrer"&gt;Shopify&lt;/a&gt; webhooks provide a one-way communication medium for Shopify to send notifications and data about events taking place in your store to an external application. In this article, we will take a more hands-on approach to learning and understanding how Shopify webhooks work.&lt;/p&gt;

&lt;p&gt;Each Shopify store comes with an administrative interface to manage the store. Webhooks can also be created and managed using this interface. In this tutorial, we are going to create a webhook, test it, and later delete it through Shopify's admin interface. &lt;/p&gt;

&lt;p&gt;Read this tutorial if you are more interested in using Shopify's webhook API.&lt;/p&gt;

&lt;p&gt;Ready? Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Shopify webhooks work
&lt;/h2&gt;

&lt;p&gt;As stated earlier, Shopify webhooks help you integrate external applications with the Shopify store by creating a channel through which Shopify sends notifications for events happening in the store to the external application.&lt;/p&gt;

&lt;p&gt;A webhook is set up on Shopify by subscribing to a &lt;strong&gt;topic&lt;/strong&gt; on your store. A &lt;strong&gt;topic&lt;/strong&gt; is an event that can take place on a Shopify store resource e.g, a &lt;code&gt;Cart&lt;/code&gt; can be &lt;code&gt;updated&lt;/code&gt;.  &lt;code&gt;Cart&lt;/code&gt; in this example is the resource while &lt;code&gt;updated&lt;/code&gt; is the event, and this &lt;strong&gt;topic&lt;/strong&gt; on Shopify is represented as &lt;code&gt;Cart/update&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When a webhook subscription has been created, an &lt;code&gt;HTTP&lt;/code&gt; request is triggered each time an event for that &lt;strong&gt;topic&lt;/strong&gt; occurs. This &lt;code&gt;HTTP&lt;/code&gt; request is sent to an endpoint on your external application. This endpoint is known as the Webhook URL and is supplied when creating a webhook on Shopify.&lt;/p&gt;

&lt;p&gt;Shopify requires that webhook URLs use the secure &lt;code&gt;HTTPS&lt;/code&gt; protocol and the &lt;code&gt;POST&lt;/code&gt; request method.&lt;/p&gt;

&lt;p&gt;Your external application receives the webhook with its payload at the webhook URL endpoint and triggers any custom processes in response to the webhook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: Logging Shopify cart events
&lt;/h2&gt;

&lt;p&gt;In this tutorial we will explore logging, a common use case for Shopify webhooks, as engineers often use webhooks to feed information into an external logging system. These logs are mostly integrated with tools that help visualize the webhook data.&lt;/p&gt;

&lt;p&gt;Here's a quick summary of the steps we need to take to achieve the Shopify webhook logging setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the workflow logger API.&lt;/li&gt;
&lt;li&gt;Generate a webhook URL that will be used to create a webhook on Shopify.&lt;/li&gt;
&lt;li&gt;Set up a webhook on Shopify.&lt;/li&gt;
&lt;li&gt;Trigger and inspect the webhook.&lt;/li&gt;
&lt;li&gt;View the webhook logged by the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's look at what you need for the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project requirements
&lt;/h3&gt;

&lt;p&gt;To set up our demo logging system, we are going to use a local &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; API with an endpoint that receives and logs a portion of the webhook payload. &lt;/p&gt;

&lt;p&gt;To follow along with this tutorial, it's required that you have the following items set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your system, version 12 or greater is fine (if you already have a local API that you prefer to use, that is fine. Just make sure it exposes a &lt;code&gt;POST&lt;/code&gt; endpoint and listens on a known port).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; installed (you can find details on installing the tool &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;An active Shopify store with access to Shopify apps (Shopify apps are required for the webhook authentication step, but more on this later).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cloning the demo API
&lt;/h3&gt;

&lt;p&gt;Clone the application repository for the API by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; shopify-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;cd &lt;/span&gt;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, you can then run the Node.js server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-shopify-webhook&lt;/code&gt;: This is the endpoint that will be receiving the Shopify webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generating a webhook URL using the Hookdeck CLI
&lt;/h3&gt;

&lt;p&gt;We now have a locally running server that contains an endpoint to receive our webhook. However, this endpoint is not accessible to the public, thus it cannot be targeted by Shopify webhooks. &lt;/p&gt;

&lt;p&gt;A mechanism is required to route our webhooks to the locally running API endpoint, and this is where the Hookdeck CLI comes in. The &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; is built specifically for working with webhooks, and it helps tunnel external HTTP requests into your local development environment by providing you with a publicly accessible HTTPS URL. You can also inspect the headers and payloads of your webhooks, as you will see later on in this tutorial.&lt;/p&gt;

&lt;p&gt;Visit Hookdeck's &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;CLI documentation&lt;/a&gt; to install and set up the tool on your operating system. For macOS users, you can run the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck/hookdeck/hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using the Windows operating system, use the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the setup process is complete, the next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port &lt;code&gt;1337&lt;/code&gt; value with the port number on which your API is running if you're not using the sample project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;em&gt;**Create new source&lt;/em&gt;* (this prompt does not show up if you have not created any previous connections)*&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;Shopify&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-shopify-webhook&lt;/em&gt;&lt;/strong&gt; (if you're using your own custom server, replace this value with your endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;Purchase Log API&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2Fom5pcwwbmbek66tiyjbx.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%2Fom5pcwwbmbek66tiyjbx.png" alt="Hookdeck CLI Ready" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're running the CLI in guest mode, you will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a webhook subscription using the Shopify admin interface
&lt;/h3&gt;

&lt;p&gt;Now that you have your webhook URL, the next step is to create a webhook subscription on Shopify. On your Shopify admin dashboard, navigate to &lt;code&gt;Settings&lt;/code&gt; →  &lt;code&gt;Notifications&lt;/code&gt; → (scroll down to) &lt;code&gt;Webhooks&lt;/code&gt;. Click the &lt;code&gt;Create webhook&lt;/code&gt; button and fill the webhook form as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event:&lt;/strong&gt; &lt;code&gt;Cart Update&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format:&lt;/strong&gt; JSON&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;em&gt;The Webhook URL printed to your Hookdeck CLI session screen&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook API version:&lt;/strong&gt; &lt;em&gt;Select the version marked **Latest&lt;/em&gt;**&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See below an example of the filled form:&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%2Fnez125c54c5nizlrxmvj.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%2Fnez125c54c5nizlrxmvj.png" alt="Add a Shopify webhook" width="712" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; to complete the webhook creation process. You will then see your newly created webhook displayed in the &lt;code&gt;Webhooks&lt;/code&gt; list as shown below:&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%2Ffmgubdvkqjhtiy8yyygk.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%2Ffmgubdvkqjhtiy8yyygk.png" alt="Webhook Created" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing your webhook
&lt;/h3&gt;

&lt;p&gt;Great! You now have your webhook logging system set up, and it's time to put it to the test. Conveniently, Shopify provides a way to send a test webhook notification, which you can do by clicking the &lt;code&gt;Send test notification&lt;/code&gt; button on your webhook row as displayed in the previous image.&lt;/p&gt;

&lt;p&gt;Click this button for your &lt;code&gt;Cart update&lt;/code&gt; webhook subscription to send a test webhook. Once sent, you will see an entry in your Hookdeck CLI session like below:&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%2Fb44n1p5fi1xc22rvog2b.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%2Fb44n1p5fi1xc22rvog2b.png" alt="Webhook successfully recieved" width="800" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the above image, the status of the request is &lt;code&gt;201&lt;/code&gt;, indicating that a new resource has been created on the logger API.&lt;/p&gt;

&lt;p&gt;To view details of the webhook, copy the event URL, which is the last item on the logged webhook in the CLI. Load this URL in your browser and you will see a screen similar to the one below:&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%2F0zszhyv2soqn6gjg2a52.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%2F0zszhyv2soqn6gjg2a52.png" alt="Event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The webhook event page provides you with in-depth details regarding the webhook you just received. Starting from the top section you can see some metadata provided by Hookdeck, shown below:&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%2Fncp5et1a7rwj8o1zmkhs.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%2Fncp5et1a7rwj8o1zmkhs.png" alt="Hookdeck summary data" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is the &lt;strong&gt;Headers&lt;/strong&gt; section. Here, you can access all the headers that came with the webhook and inspect their values:&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%2Fh5mtccncj82hvrtaihdr.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%2Fh5mtccncj82hvrtaihdr.png" alt="Event headers" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the webhook payload, the &lt;strong&gt;Body&lt;/strong&gt; section contains a browsable object representing the webhook payload. You can expand this object to inspect all the properties contained in the data that came in the payload:&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%2Fwd8rocipkteefdlg7n9b.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%2Fwd8rocipkteefdlg7n9b.png" alt="Event body" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Viewing the webhook logs
&lt;/h3&gt;

&lt;p&gt;So, we have successfully received a webhook on our logging application. Let's now check the application logs to confirm that our webhook is indeed being logged. To have an appreciable amount of webhooks logged, click the &lt;code&gt;Send test notification&lt;/code&gt; button a few more times.&lt;/p&gt;

&lt;p&gt;Then, on your browser, navigate to the &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt; endpoint of the locally running API, and you will see a display similar to the one below:&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%2Fngcvib0vz786wojaski5.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%2Fngcvib0vz786wojaski5.png" alt="Webhooks logs" width="629" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the above image, we have 3 webhooks logged (the first entry is a test entry already contained in the database). We have been able to log the webhook ID, the total line items in the cart, and the time the webhook was triggered for each webhook. Now, it's time to verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to verify Shopify webhooks with HMAC Signature
&lt;/h2&gt;

&lt;p&gt;Webhooks create a communication medium between your external apps and Shopify, so you need to make sure that only Shopify is able to use this medium to speak to your app. Attackers often take advantage of the open-ended nature of a webhook URL to send requests containing malicious content to the webhook endpoint.&lt;/p&gt;

&lt;p&gt;Fortunately, in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header sent with each webhook, Shopify sends an encrypted version of the payload. This encrypted value is created using your Shopify app's shared secret key and the &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC cryptographic algorithm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can get a Shopify shared secret for your store by creating a Shopify application in the &lt;code&gt;Apps&lt;/code&gt; section. The shared key is contained in the &lt;code&gt;Admin API&lt;/code&gt; section of the app created.&lt;/p&gt;

&lt;p&gt;When you receive a webhook, you can compute the same encrypted version of the payload using the shared key and HMAC, and then compare the result with the value sent in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header. If there is a match, then the payload is valid and it originated from Shopify. If not, then it's suspicious and should be ignored.&lt;/p&gt;

&lt;p&gt;You can find this verification logic in the &lt;code&gt;server.js&lt;/code&gt; file as a commented middleware function &lt;code&gt;validatePayload&lt;/code&gt;, shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-shopify-hmac-sha256&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;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&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;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;xx-xx-x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;.....&lt;/span&gt;

&lt;span class="cm"&gt;/* function validatePayload(req, res, next) {
    if(req.method == "POST"){
        if (!req.rawBody) {
            return next('Request body empty')
        }
        const body = req.rawBody;
        const hmacHeader = req.get(sigHeaderName);
        //Create a hash based on the parsed body
        const hash = crypto
            .createHmac(sigHashAlg, secret)
            .update(body, "utf8", "hex")
            .digest("base64");
        // Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header
        if (hash !== hmacHeader) {
            return next(`Request body digest (${hash}) did not match ${sigHeaderName} (${hmacHeader})`)
        } 
    }
    return next()
}
app.use(validatePayload); */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on security considerations for your webhooks, check out our &lt;a href="https://hookdeck.com/guides/webhooks/webhooks-security-checklist" rel="noopener noreferrer"&gt;security checklist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to delete Shopify webhooks
&lt;/h2&gt;

&lt;p&gt;Deleting webhooks from your Shopify store is simple and straightforward using the admin interface. Simply click the bin icon next to your webhook on the webhooks list:&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%2Fzcplcmc6d5wnf20036o1.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%2Fzcplcmc6d5wnf20036o1.png" alt="Image description" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shopify will prompt you for to confirm to ensure that you really want to take this action. Note that once a webhook is deleted, it cannot be recovered again. &lt;/p&gt;

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

&lt;p&gt;The Shopify administrative interface provides easy-to-use tools to create and manage your store webhooks. In this tutorial, you have learned and demonstrated how to use the Shopify admin interface to set up a webhook, test it, and then delete the subscription. &lt;/p&gt;

&lt;p&gt;If you like doing things programmatically, or want to set up automated tasks like scheduled cron jobs to interact with Shopify webhooks and other store resources, Shopify also provides an API that you can call. We have detailed how you can authenticate with Shopify's API and use it to set up your webhooks in this article.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webhook</category>
      <category>shopify</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify Webhooks Best Practices</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:16:04 +0000</pubDate>
      <link>https://forem.com/hookdeck/shopify-webhooks-best-practices-11oh</link>
      <guid>https://forem.com/hookdeck/shopify-webhooks-best-practices-11oh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this series, we've explored how to &lt;a href="https://hookdeck.com/guides/platforms/post/getting-started-shopify-webhooks-guide" rel="noopener noreferrer"&gt;get started with Shopify webhooks&lt;/a&gt;, and shared step-by-step tutorials on how to create webhooks using both the Shopify &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-admin-dashboard-tutorial" rel="noopener noreferrer"&gt;admin dashboard&lt;/a&gt; and &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-api-tutorial#tutorial-prerequisites" rel="noopener noreferrer"&gt;Shopify API.&lt;/a&gt;  Now, we'll conclude the series by looking at some important best practices for building resilience and reliability into Shopify webhooks in a production environment. We want reliable Shopify webhooks because sudden spikes in your Shopify webhook traffic can overload your API and cause it to shut down if the capacity of your API server is exhausted. Shopify can also send duplicate webhooks that could potentially cause inconsistencies in your application database.&lt;/p&gt;

&lt;p&gt;In this article, we will share and suggest some key engineering practices that will help make your Shopify webhooks resilient to the usage pressures common in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopify webhooks features
&lt;/h2&gt;

&lt;p&gt;Before getting into best practices for Shopify webhooks, let's take a look at its features. Understanding the strengths and weaknesses of Shopify webhooks and how they operate helps engineers design the most efficient solutions to mitigate any issues that might arise when using them.&lt;/p&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;Related Best Practice&lt;/th&gt;
&lt;th&gt;Related Best Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Webhook Configuration&lt;/td&gt;
&lt;td&gt;Admin API and Shopify admin (REST or GraphQL)&lt;/td&gt;
&lt;td&gt;Integration development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook URL&lt;/td&gt;
&lt;td&gt;Allow one per subscription&lt;/td&gt;
&lt;td&gt;Integration development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response Format&lt;/td&gt;
&lt;td&gt;JSON or XML&lt;/td&gt;
&lt;td&gt;Integration development, troubleshooting and testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request Method&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Integration development, troubleshooting and testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browsable Log&lt;/td&gt;
&lt;td&gt;Delivery metrics on your partner dashboard&lt;/td&gt;
&lt;td&gt;Troubleshooting and testing, failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hashing Algorithm&lt;/td&gt;
&lt;td&gt;SHA256&lt;/td&gt;
&lt;td&gt;Security and verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual Retry&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic Retry Logic&lt;/td&gt;
&lt;td&gt;Exponential, 19 times over 48 hours&lt;/td&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alert Logic&lt;/td&gt;
&lt;td&gt;Shopify sends an alert for each failure right before it deletes any webhooks&lt;/td&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeout period&lt;/td&gt;
&lt;td&gt;5 seconds&lt;/td&gt;
&lt;td&gt;Scalability with asynchronous processing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now that you're familiar with the features of Shopify webhooks, let's go ahead and break down solutions to issues that may arise in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopify webhooks best practices checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Local troubleshooting and testing
&lt;/h3&gt;

&lt;p&gt;Properly testing your Shopify webhooks helps you avoid all the performance issues that arise due to buggy code. For example, let's say your endpoint on an external accounting application assumes that Shopify will send the total price for a store order as an integer value (e.g. 10). Meanwhile, Shopify sends the price as a floating-point number (10.15). Your accounting application then forcefully coerces the floating number into an integer value which does not reflect the true cost of the order, and will lead to inconsistencies in the accounting data.&lt;/p&gt;

&lt;p&gt;You need to test your Shopify webhook endpoint to ensure the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The endpoint exists (to avoid 404 errors)&lt;/li&gt;
&lt;li&gt;The endpoint does not perform a redirect (Shopify sees a redirect as an error)&lt;/li&gt;
&lt;li&gt;The logic on the endpoint is bug-free and has the desired impact on your application&lt;/li&gt;
&lt;li&gt;You return a proper status code for successfully processing the webhook (2xx range of HTTP status code) and for failure (4xx, 5xx range of HTTP status codes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One drawback in testing Shopify webhooks is that webhooks cannot be received in a local development environment because they require a publicly accessible URL. Fortunately, tools like the &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; exist to solve this problem by tunneling your Shopify webhook requests to your local environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asynchronous processing to ensure reliability
&lt;/h3&gt;

&lt;p&gt;When Shopify sends a webhook, it waits for a response from the receiving application. This waiting period elapses after 5 seconds. Thus, if Shopify does not receive a response within the waiting period, Shopify assumes that the webhook delivery has failed.&lt;/p&gt;

&lt;p&gt;As a best practice, you need to respond to Shopify as fast as possible. This practice is recommended on &lt;a href="https://shopify.dev/apps/webhooks#respond-to-webhooks-quickly" rel="noopener noreferrer"&gt;Shopify's webhook documentation&lt;/a&gt;. That said, it might not be possible to respond so quickly under certain conditions, such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Long-running processes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your webhook can trigger a time-consuming task, such as file encryption, database queries, or computations that take long periods to complete. It may be difficult, and sometimes impossible, to send a response within the 5-second limit in this situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Multiple webhooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's assume that the time it takes your application to process a single webhook is 3 seconds; you would be able to return a response within the 5-second limit. However, if your application receives 3 webhooks at the same time, the time required becomes 9 seconds and the second webhook will not complete before it times out.&lt;/p&gt;

&lt;p&gt;Imagine you have a Shopify store that sells flowers, and you process an average of 10 orders daily. You have set up a webhook that targets an external delivery service when a customer places an order. Your delivery service comfortably handles your daily average load and then suddenly, Valentine's Day season rolls by, and the number of orders for flowers on your store surges to an all-time high of 300 orders per day.&lt;/p&gt;

&lt;p&gt;You start receiving 300 webhooks daily on your delivery service, which causes the server resources for the application to be used up quickly and drop webhooks due to service failure. As webhooks begin to drop, many customers that have ordered and paid for their flowers won't have their flowers delivered.&lt;/p&gt;

&lt;p&gt;So, how do we mitigate these often unavoidable situations? One of the standard solutions is to boost processing speed by expanding the server hardware capabilities. You can also adopt horizontal scaling techniques to distribute your webhook workload. &lt;/p&gt;

&lt;p&gt;The above solutions are good, but the best way to respond to Shopify as quickly as possible is by processing the webhooks asynchronously. &lt;/p&gt;

&lt;p&gt;With &lt;a href="https://hookdeck.com/blog/post/introduction-asynchronous-processing" rel="noopener noreferrer"&gt;asynchronous processing&lt;/a&gt;, Shopify does not need to wait for your application to finish processing a webhook before it receives a response. &lt;/p&gt;

&lt;p&gt;One of the ways to implement asynchronous processing for Shopify webhooks is by placing a &lt;a href="https://hookdeck.com/blog/post/introduction-message-queue" rel="noopener noreferrer"&gt;message broker&lt;/a&gt; between Shopify and your application. The message broker ingests webhooks from Shopify and immediately sends a response back to Shopify. The broker then routes the webhooks to your application at a rate that your application can handle.&lt;/p&gt;

&lt;p&gt;This way, your Shopify webhooks never time out, and your application does not run out of server resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying your incoming webhooks
&lt;/h3&gt;

&lt;p&gt;The next best practice we'll look at is security. The HTTPS endpoint used by your applications to receive Shopify webhooks is publicly accessible, which means that any client can call it.&lt;/p&gt;

&lt;p&gt;This open-ended nature of your webhook URL is a huge security flaw, as attackers can spoof webhook requests to your application for various malicious purposes. You need a way to prevent your webhook endpoints from receiving requests that are not from Shopify.&lt;/p&gt;

&lt;p&gt;Let's imagine that you have a Shopify store that sells basketball jerseys, with a webhook set up to message a delivery service about the order details for the jersey to be delivered to the customer's address. Without verification, an attacker can spoof order webhooks to your delivery service webhook URL and get free jerseys anytime they want.&lt;/p&gt;

&lt;p&gt;Fortunately, Shopify provides a way to help us with this. Shopify sends a hashed version of the body of the payload in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header (if you're using Ruby on Rails or Sinatra, the header is &lt;code&gt;HTTP_X_SHOPIFY_HMAC_SHA256&lt;/code&gt; ). The value of this header is a base64 encoded string and serves as the digital signature for the webhook payload. This signature is signed using your Shopify app's shared secret. For more information about Shopify app secrets and how to generate one, check the &lt;a href="https://shopify.dev/apps/auth/rotate-revoke-api-credentials" rel="noopener noreferrer"&gt;docs page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To verify the payload on your endpoint handler, you need to re-compute the signature using your Shopify secret, the webhook payload, and some &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC algorithm&lt;/a&gt; functions. Once you have your version of the payload signature, you can then compare it with the one sent in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; to confirm a match.&lt;/p&gt;

&lt;p&gt;Below is an example of the verification process written in Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRawBody&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;raw-body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//install raw-body from npm&lt;/span&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="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="nx"&gt;MY_SHOPIFY_SECRET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//Extract X-Shopify-Hmac-Sha256 Header from the request&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmacHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Shopify-Hmac-Sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Parse the request Body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getRawBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//Create a hash based on the parsed body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&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="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;body&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="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="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;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;hmacHeader&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;Webhook source confirmed. Continue processing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;Unidentified webhook source. Do not process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also worth mentioning that you should store your Shopify secret in an environment variable, especially in production environments. The entire process of verifying your webhook breaks down if an attacker can get hold of your Shopify secret.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idempotency in webhook processing
&lt;/h3&gt;

&lt;p&gt;Idempotency helps prevent the negative impact of processing a webhook more than once. Let's imagine that we need to deduct the price of a purchased item from a customer's wallet (the wallet application is external to the Shopify store). A charge is deducted from the wallet when we receive a webhook confirming the purchase. If this process is done more than once for a single purchase, the customer's wallet is now in a state that is inconsistent with their purchase history.&lt;/p&gt;

&lt;p&gt;Situations like this negatively impact the integrity of your application's data, and in extension, the Shopify store itself.&lt;/p&gt;

&lt;p&gt;While some activities, like updating the status of an order, are by nature idempotent, others like the one described above are not. You need a mechanism built into your webhook processing operations to ensure that no webhook is processed more than once. &lt;/p&gt;

&lt;p&gt;Shopify helps to achieve this by sending the unique identifier for a webhook in the &lt;code&gt;X-Shopify-Webhook-Id&lt;/code&gt;. With this identifier, you can track webhooks that have already been processed and skip them to avoid duplicating their impact on your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with delayed webhooks
&lt;/h3&gt;

&lt;p&gt;On rare occasions, Shopify might send a webhook late (sometimes up to a day late). If the data in the payload is very critical to your application, you should verify how recent the data is. Stale data has the potential to cause state inconsistencies within your application, thereby compromising the database.&lt;/p&gt;

&lt;p&gt;For example, think of an external inventory system where the quantity of an item is updated (i.e. deducted) after a successful purchase on Shopify. If the item is no longer available (&lt;code&gt;quantity = 0&lt;/code&gt; in the inventory system) and you have a late purchase webhook coming in, the current quantity in the inventory will either be negative, undefined, or default to zero based on the business logic implemented. However, this new inventory value does not reflect the actual state of the item quantity. &lt;/p&gt;

&lt;p&gt;Shopify does its best to send the most recent data with a webhook even when the webhook delays. As an engineer, you know that assumptions can easily lead to bugs, so you want to make sure you're verifying how recent the webhook data is despite the promise by Shopify.&lt;/p&gt;

&lt;p&gt;One way to implement this verification is by comparing the timestamp of the webhook to the current time. If the timestamp on the webhook is behind the current time, you can call the Shopify API to request the current data. This check ensures that you're getting the most up-to-date data for the Shopify resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing reconciliation jobs
&lt;/h3&gt;

&lt;p&gt;This best practice falls under the housekeeping category. Webhooks are sent to notify your external application and send real-time data about an event on a Shopify resource. However, Shopify advises that you &lt;a href="https://shopify.dev/apps/webhooks#implement-reconciliation-jobs" rel="noopener noreferrer"&gt;shouldn't solely rely on webhooks&lt;/a&gt; because, and this is very rare, the webhook for a particular event might not be sent.&lt;/p&gt;

&lt;p&gt;An example of a scenario where a reconciliation job is required would be if you had a Shopify store selling shirts and an external accounting application integrated with your store using webhooks.&lt;/p&gt;

&lt;p&gt;Let's assume that you need to balance your accounts in the external accounting application at the end of each business day. A reconciliation job can be scheduled to run 30 minutes before you query your transaction history to compute your balances. This job can be used to pull all purchase information from your Shopify store and synchronize it with your accounting application so that you have all the data you need up-to-date.&lt;/p&gt;

&lt;p&gt;Thus, to mitigate the issue of unsent Shopify webhooks, you should implement reconciliation jobs to synchronize your application data with Shopify. These reconciliation jobs are scheduled tasks that periodically fetch data from Shopify for specific resources.&lt;/p&gt;

&lt;p&gt;Reconciliation jobs should be set up for sensitive data within your application; these are data that need to be up-to-date at certain periods while your application is running.&lt;/p&gt;

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

&lt;p&gt;Building reliability and resilience into your infrastructure ensures that your setup can withstand the pressures of production environments. In this article, we have looked at some best practices to follow to ensure that our Shopify webhooks function without issues. This list is not set in stone, and you're encouraged to take more precautions that help prevent your application integration with Shopify from falling short of expectations.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>showdev</category>
      <category>webhook</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Shopify Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:04:22 +0000</pubDate>
      <link>https://forem.com/hookdeck/getting-started-with-shopify-webhooks-19id</link>
      <guid>https://forem.com/hookdeck/getting-started-with-shopify-webhooks-19id</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Shopify is a great e-commerce SaaS application that offers a lot of flexibility and in turn helps developers create custom integrations. By using the API, merchants can programmatically interface their store with their custom applications. On top of the API, Shopify has introduced webhooks, a way to transfer event data from stores to custom integrations in real-time.&lt;/p&gt;

&lt;p&gt;In this article, we will take a tour of Shopify webhooks to learn what they are all about, how to set them up, and how to find out more information on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Shopify webhook?
&lt;/h2&gt;

&lt;p&gt;Shopify webhooks are a one-way communication system. When an event occurs for a subscription to a Shopify topic (e.g. &lt;code&gt;orders/create&lt;/code&gt;), an &lt;code&gt;HTTPS&lt;/code&gt; request is triggered. This &lt;code&gt;HTTPS&lt;/code&gt; request is then sent to an endpoint on the receiving application. &lt;/p&gt;

&lt;p&gt;A Shopify &lt;strong&gt;topic&lt;/strong&gt; is an event that can take place on a Shopify resource. In the &lt;code&gt;orders/create&lt;/code&gt; example stated above, the resource is&lt;code&gt;orders&lt;/code&gt;, and &lt;code&gt;create&lt;/code&gt; is the event.&lt;/p&gt;

&lt;p&gt;To understand things better, let's use a simple example to describe the webhook communication process. &lt;/p&gt;

&lt;p&gt;A Shopify merchant that sells jerseys wants to send notifications for jersey orders on their Shopify store to an external delivery service. This delivery service is responsible for processing the delivery of the jersey to the customer.&lt;/p&gt;

&lt;p&gt;The Shopify merchant goes into their Shopify admin dashboard (or uses the API) to create a &lt;strong&gt;subscription&lt;/strong&gt; for the &lt;code&gt;orders/create&lt;/code&gt; &lt;strong&gt;topic&lt;/strong&gt; by submitting an endpoint on an external application where the webhook is to be sent. A customer visits the store and places an order. Once this order is created, the event for the &lt;code&gt;orders/create&lt;/code&gt; &lt;strong&gt;topic&lt;/strong&gt; is fired. Because there is a webhook &lt;strong&gt;subscription&lt;/strong&gt; for this &lt;strong&gt;topic&lt;/strong&gt;, an &lt;code&gt;HTTPS&lt;/code&gt; request is immediately triggered to the external application's endpoint.&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%2Fm78ke9y1v0l5lftsnaca.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%2Fm78ke9y1v0l5lftsnaca.png" alt="How Shopify webhooks work" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Shopify webhook request consists of different parts. The webhook headers contain metadata about the webhook, and the payload contains the actual Shopify event information. The payload can be sent in either JSON or XML format. The format your webhook payload is sent as can be configured on Shopify's admin interface or through the Shopify API.&lt;/p&gt;

&lt;p&gt;It is important to note that to receive webhooks, Shopify requires a publicly accessible &lt;code&gt;HTTPS&lt;/code&gt; endpoint. Tunneling solutions like &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck's CLI&lt;/a&gt; and &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt; provide a way to generate an &lt;code&gt;HTTPS&lt;/code&gt; endpoint that channels webhook requests to your development environment. &lt;/p&gt;

&lt;h2&gt;
  
  
  Supported Shopify webhook events
&lt;/h2&gt;

&lt;p&gt;Shopify webhooks help applications that want to stay in sync with Shopify by updating internal states or executing code based on an event that occurs in a store. Applications that are often integrated with Shopify using webhooks include invoicing systems, accounting apps, and mailing/notification services, among other things.&lt;/p&gt;

&lt;p&gt;Merchants can integrate Shopify webhooks to monitor sales, update inventory, perform accounting operations and synchronization, and also perform many more auditory and sales management processes. Making the right integration involves knowing the right event to subscribe to, so let's look at Shopify webhook events and how we can make use of them.&lt;/p&gt;

&lt;p&gt;An event is fired when an action for a Shopify &lt;strong&gt;topic&lt;/strong&gt; (e.g. &lt;code&gt;cart/update&lt;/code&gt; topic, which means a Shopify cart is updated) takes place on Shopify. This action can be performed by the store admin, a customer, or Shopify itself.&lt;/p&gt;

&lt;p&gt;Shopify supports a lot of events that webhooks can subscribe to. These events are namespaced by the resource that fires the event; for example, we have the &lt;code&gt;Orders/closed&lt;/code&gt; and &lt;code&gt;Orders/cancelled&lt;/code&gt; events belonging to the &lt;code&gt;Orders&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;Most e-commerce integrations focus on the &lt;code&gt;Orders&lt;/code&gt; events as they generate webhooks for events involved in the process of purchasing items from the Shopify store. There are also events for resources like &lt;code&gt;Cart&lt;/code&gt;, &lt;code&gt;Checkout&lt;/code&gt;, &lt;code&gt;Customer&lt;/code&gt;, &lt;code&gt;InventoryItem&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Let's take a look at some of these resource events, as well as example scenarios where they can be used in our application workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cart events
&lt;/h3&gt;

&lt;p&gt;These events are fired when a customer creates or updates items in their shopping cart. The two available cart events are &lt;code&gt;carts/create&lt;/code&gt; and &lt;code&gt;carts/update&lt;/code&gt;. One potential use case for these events is to query an external inventory to check if an item is still in stock. You can also use these events as ping signals to an external inventory application in order to sync the number of items in the inventory with the number of items on your Shopify store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Order events
&lt;/h3&gt;

&lt;p&gt;Order events are fired when a Shopify order is created, updated, paid for, or deleted. Events under this category include: &lt;code&gt;orders/cancelled&lt;/code&gt;, &lt;code&gt;orders/create&lt;/code&gt;, &lt;code&gt;orders/delete&lt;/code&gt;, &lt;code&gt;orders/edited&lt;/code&gt;, &lt;code&gt;orders/fulfilled&lt;/code&gt;, &lt;code&gt;orders/paid&lt;/code&gt;, &lt;code&gt;orders/partially_fulfilled&lt;/code&gt;, and &lt;code&gt;orders/updated&lt;/code&gt;. Below are some scenarios where you can use webhooks for these events: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reverting a payment through an external payment platform when a customer cancels an order on your Shopify store (&lt;code&gt;orders/cancelled&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Notifying a delivery service once a customer pays for an item they ordered from your Shopify store (&lt;code&gt;orders/paid&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Locking items that have been ordered in an external inventory application to ensure that preceding orders do not assume the wrong availability of an item or a wrong quantity of items (&lt;code&gt;orders/create&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dispute events
&lt;/h3&gt;

&lt;p&gt;These are fired when a customer creates or updates an issue (complaint or comment) relating to the service they got from your store on Shopify. The two events under this category are &lt;code&gt;disputes/create&lt;/code&gt; and &lt;code&gt;disputes/update&lt;/code&gt;. One use case for these events is to create and update issues on an external issue tracker like &lt;a href="https://gorgias.com" rel="noopener noreferrer"&gt;Gorgias&lt;/a&gt;. Services like Gorgias have more customer support features built-in than what is available on Shopify.&lt;/p&gt;

&lt;p&gt;To effectively take advantage of what Shopify webhooks have to offer, it is important to have a good knowledge of the Shopify events that you can subscribe for. This knowledge will help you build the proper workflows for webhooks within your application or third-party integrations.&lt;/p&gt;

&lt;p&gt;It is also important to know that not all Shopify resources generate events. For a detailed list of Shopify events, check out the &lt;a href="https://shopify.dev/api/admin-rest/2021-10/resources/webhook#top" rel="noopener noreferrer"&gt;documentation page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a webhook using the Shopify admin interface
&lt;/h2&gt;

&lt;p&gt;Now that you have a good understanding of Shopify webhooks and how they work, and you have chosen them as a solution to a project you're working on, let's look at how to create one in the Shopify admin interface. You can also follow along if you just want to get some practical knowledge on setting up webhooks on Shopify.&lt;/p&gt;

&lt;p&gt;The easiest way to set up a webhook on your Shopify store is through the admin interface. To locate the admin interface, navigate to &lt;code&gt;https://[MY_STORE_ADDRESS].com/admin&lt;/code&gt;. From the admin interface, go to &lt;code&gt;Settings &amp;gt; Notifications &amp;gt; Webhooks&lt;/code&gt;. Click the &lt;code&gt;Create Webhook&lt;/code&gt; button and fill the webhook form by selecting an event, the payload format, and entering your application endpoint (i.e. the endpoint on the application where you will be receiving your Shopify webhooks) into the &lt;code&gt;URL&lt;/code&gt; field as shown below:&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%2F6c4rc53qxi6mtroxblgh.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%2F6c4rc53qxi6mtroxblgh.png" alt="Add a Shopify webhook - Admin" width="712" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensure you select one of the stable API versions then click the &lt;code&gt;Save&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;You will now receive a webhook request on the endpoint you supplied whenever the selected event occurs in your Shopify store.&lt;/p&gt;

&lt;p&gt;For a more detailed hands-on tutorial on using the Shopify admin interface to create and manage your webhooks, &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-admin-dashboard-tutorial" rel="noopener noreferrer"&gt;check out this article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Shopify webhooks API
&lt;/h2&gt;

&lt;p&gt;Webhook subscriptions cannot &lt;em&gt;only&lt;/em&gt; be created from the Shopify Admin interface — you can also create and manage your webhooks dynamically by calling the Shopify webhooks API! The Shopify API comes in two forms: the very common &lt;a href="https://shopify.dev/apps/webhooks" rel="noopener noreferrer"&gt;REST API&lt;/a&gt; and a &lt;a href="https://shopify.dev/api/admin-graphql/2021-10/enums/webhooksubscriptiontopic" rel="noopener noreferrer"&gt;GraphQL variant&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The REST API consists of endpoints that you can use to create a webhook, retrieve webhooks, modify a webhook and also remove a webhook. The GraphQL API also consists of &lt;code&gt;queries&lt;/code&gt; and &lt;code&gt;mutations&lt;/code&gt; that mirror the same functionalities you get from the REST API.&lt;/p&gt;

&lt;p&gt;To use the Shopify webhooks API, you need to be &lt;a href="https://shopify.dev/api/admin-rest#authentication" rel="noopener noreferrer"&gt;authenticated&lt;/a&gt;. Shopify also provides a good number of &lt;a href="https://shopify.dev/api/admin-rest#client_libraries" rel="noopener noreferrer"&gt;client libraries&lt;/a&gt; for languages like PHP, Node.js, and Python that make the developer experience a lot better.&lt;/p&gt;

&lt;p&gt;For more information and a detailed analysis of using the Shopify API to manage your webhooks, check out our &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-api-tutorial" rel="noopener noreferrer"&gt;tutorial on using the Shopify webhooks API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding information on the Shopify webhooks documentation
&lt;/h2&gt;

&lt;p&gt;The Shopify documentation contains a great deal of information on webhooks; however, it can be a bit cumbersome moving around tons of documentation pages to find the exact information you are looking for. I have made a simple list below to help you easily find enough information to start using Shopify webhooks as a professional:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Why you should check it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Overview&lt;/td&gt;
&lt;td&gt;If you are looking for more information on Shopify webhooks, this overview will be useful in helping to deepen your understanding of Shopify webhooks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopify Webhook Configuration&lt;/td&gt;
&lt;td&gt;This page contains a simple list of steps to take when configuring your webhook workflow.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting up your endpoint to receive webhooks&lt;/td&gt;
&lt;td&gt;If you're setting up a Shopify webhook for the first time, this page takes you through a step by step process showing how to get your webhook set up properly. You learn how to prepare your endpoint to receive webhooks, how to test your webhook connection, and also how you can verify your webhook source.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API documentation&lt;/td&gt;
&lt;td&gt;If you are looking to go down the API route, this page furnishes you with all the information about available endpoints, requesting an response format, supported events and more.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API versions&lt;/td&gt;
&lt;td&gt;Over the years, different versions of the Shopify webhooks API have been released. If you're working on a project that uses older versions of the API, this page helps you manage different API versions in your code, and also teaches you how to upgrade to the latest version.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;A large amount of activities on the internet are e-commerce related, and Shopify plays a huge part in that space. Shopify may have taken care of a lot of the basic requirements of an e-commerce store, but the platform's capacity for extensions makes it one of the most preferred choices for setting up an online store. &lt;/p&gt;

&lt;p&gt;In this article, you have learned about Shopify webhooks and how they enable you to build custom integrations with the platform. This way, you can complement any missing feature in Shopify or substitute it with a better or preferred alternative.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>shopify</category>
      <category>showdev</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Troubleshooting GitHub Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Mon, 01 Nov 2021 13:43:58 +0000</pubDate>
      <link>https://forem.com/hookdeck/troubleshooting-github-webhooks-5aoi</link>
      <guid>https://forem.com/hookdeck/troubleshooting-github-webhooks-5aoi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a previous article, I demonstrated the use of GitHub webhooks in a simple tutorial using a sample API that logged webhook events. In this article, we will learn how to debug common issues with GitHub webhooks using the same sample project. This time, I have introduced some bugs into the project that we are going to learn how to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting GitHub webhooks: Requirements checklist
&lt;/h2&gt;

&lt;p&gt;To begin there are a few things you need to have or set up, including: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub repository&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your system to run the sample project&lt;/li&gt;
&lt;li&gt;A publicly accessible URL to the API endpoint. We will use the &lt;a href="https://hookdeck.com/cli#installation" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; to achieve this. To install the Hookdeck CLI, check out this &lt;a href="https://hookdeck.com/cli#installation" rel="noopener noreferrer"&gt;page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Clear visualization of error messages. The Hookdeck CLI will help us with event pages where error details can be inspected.&lt;/li&gt;
&lt;li&gt;A text editor for editing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this setup, you have an environment in which you can conveniently troubleshoot your GitHub webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting GitHub webhooks with Hookdeck
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cloning and running a demo API
&lt;/h3&gt;

&lt;p&gt;The sample API we will use is available on the &lt;a href="https://github.com/hookdeck/nodejs-webhook-server-example" rel="noopener noreferrer"&gt;Hookdeck GitHub repository&lt;/a&gt;. Follow the instructions to set it up.&lt;/p&gt;

&lt;p&gt;Clone the repository by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; github-webhooks-debugging https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;cd &lt;/span&gt;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, run the Node.js server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-github-webhook&lt;/code&gt;: This is the endpoint that will be receiving the GitHub webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting a GitHub webhook URL
&lt;/h3&gt;

&lt;p&gt;The next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;strong&gt;&lt;em&gt;Create new source&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;GitHub&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-github-webhook&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;My Github Response Server&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2Flhlksgarz8hqumpdbnrt.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%2Flhlksgarz8hqumpdbnrt.png" alt="CLI Ready" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confirming webhook delivery
&lt;/h3&gt;

&lt;p&gt;To begin receiving webhooks, you need to set up a webhook on your GitHub repository. &lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Settings → Webhooks&lt;/strong&gt;. On the &lt;strong&gt;Webhooks&lt;/strong&gt; page, click on the &lt;code&gt;Add webhook&lt;/code&gt; button on the top right-hand corner.&lt;/p&gt;

&lt;p&gt;On the webhook form displayed, paste the webhook URL generated by the Hookdeck CLI into the &lt;code&gt;Payload URL&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Fill the remaining fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content type:&lt;/strong&gt; Select &lt;code&gt;application/json&lt;/code&gt; so that you can receive the payload as a JSON object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret:&lt;/strong&gt; You can leave this blank, but for this tutorial enter &lt;code&gt;1234ABCD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Verification:&lt;/strong&gt; Leave this as the default option, &lt;code&gt;Enable SSL verification&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which events would you like to trigger this webhook:&lt;/strong&gt; This is the point where you subscribe for a GitHub event on your repository. For this tutorial, select the &lt;code&gt;Just the push event&lt;/code&gt; option as we are only subscribing to the &lt;code&gt;push&lt;/code&gt; event. You can either subscribe for all events or a subset of the events using the other two options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active:&lt;/strong&gt; Leave this checked to receive event details when the GitHub webhook is triggered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the complete webhook form below:&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%2Fr123fgxn3s1q2chiv651.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%2Fr123fgxn3s1q2chiv651.png" alt="Add Webhook - GitHub" width="800" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Add webhook&lt;/code&gt; button to complete the process.&lt;/p&gt;

&lt;p&gt;With this setup, anytime you push code to your repository, a webhook request will be fired to the specified webhook URL.&lt;/p&gt;

&lt;p&gt;Immediately after you complete registration for a webhook in the step above, a &lt;code&gt;ping&lt;/code&gt; webhook request will be fired to your webhook URL. Because we are using the Hookdeck CLI, we would already see the &lt;code&gt;ping&lt;/code&gt; webhook request logged on the terminal where the Hookdeck CLI is running, as shown below:&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%2Fo7ko40wv71qug723k0a1.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%2Fo7ko40wv71qug723k0a1.png" alt="404 - CLI" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This confirms that we are successfully receiving our webhook request. However, did you notice the status code indicated on the webhook request? Yeah, a &lt;code&gt;404&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's take a look at how to deal with this error in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting GitHub webhook "not found" error
&lt;/h3&gt;

&lt;p&gt;Our first webhook attempt resulted in a &lt;code&gt;404&lt;/code&gt; error on the destination endpoint. This tells us that even though we are successfully receiving our webhooks, it cannot locate the endpoint specified, or the endpoint does not exist. &lt;code&gt;404&lt;/code&gt; errors can be fixed, most of the time, by checking for typos or misspellings in the specified route name. Worst case scenario, the route truly does not exist and needs to be created.&lt;/p&gt;

&lt;p&gt;Use the event page link displayed on the CLI to go to the event page, where you will see a page similar to the one below:&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%2Fu8s4ru2tdvpnee1dtdmt.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%2Fu8s4ru2tdvpnee1dtdmt.png" alt="404 event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the red status code badge under the &lt;strong&gt;Attempts&lt;/strong&gt; section and click on it to reveal details about the error on the right-hand side of the screen, as shown below:&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%2F778ljhynbrm8mvuczd31.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%2F778ljhynbrm8mvuczd31.png" alt="404 Server response" width="405" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This helps us see the actual response from the running server, and it also confirms that the specified endpoint cannot be found.&lt;/p&gt;

&lt;p&gt;The endpoint specified for the webhook to hit is &lt;code&gt;/log-github-webhook&lt;/code&gt;, which can be found in the &lt;code&gt;routes.js&lt;/code&gt; file at the root of the project directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/log-github-webhoo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//console.log(req.body);&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;webhook_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;repo&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="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;author&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="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;time&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="nx"&gt;head_commit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&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;save_webhook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webhooks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webhook_info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook Event successfully logged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taking a close look at the route handler, you will notice that there is a typo just at the end of the endpoint name: a missing '&lt;strong&gt;k&lt;/strong&gt;'. Simply add the missing character and save the file.&lt;/p&gt;

&lt;p&gt;You will need to restart the Node.js server to allow the changes to take effect.  Shut down the server using &lt;code&gt;Ctrl + C&lt;/code&gt; and restart it with the &lt;code&gt;npm start&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Now, fire a new webhook to your endpoint by pushing a commit to your GitHub repository. Once this is done, check your Hookdeck CLI session for the new webhook received. You will see an entry similar to the one below:&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%2Ftrpeppghq4vxz6c2obfn.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%2Ftrpeppghq4vxz6c2obfn.png" alt="401 - CLI" width="800" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen from the above webhook received, we have been able to clear the &lt;code&gt;404&lt;/code&gt; error. However, we now have a new error: a &lt;code&gt;401&lt;/code&gt; error. Let's deal with this in the following section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting GitHub webhook error 401
&lt;/h3&gt;

&lt;p&gt;HTTP &lt;code&gt;401&lt;/code&gt; errors indicate that an unauthorized action is being performed. Before making any assumptions, let's inspect the server response using the event's page. Copy the event page link from the Hookdeck CLI session and load it in your browser, and you will see a screen similar to the one below:&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%2Fd2vzxzxdi3adp5idn8gf.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%2Fd2vzxzxdi3adp5idn8gf.png" alt="401 Event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the red status code badge under the &lt;strong&gt;Attempts&lt;/strong&gt; section and then click on it. You will see the server response details on the right-hand side as shown below:&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%2Fn8hiefrxxdgk3jeyztg8.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%2Fn8hiefrxxdgk3jeyztg8.png" alt="401 server response" width="427" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reading the response message, we can see that we are failing the security check on the webhook payload. Because we defined an API &lt;code&gt;secret&lt;/code&gt;, GitHub sends the &lt;code&gt;X-Hub-Signature-256&lt;/code&gt; header. This header contains an encrypted version of the real payload and has to be validated against the unencrypted payload received by using the secret key.&lt;/p&gt;

&lt;p&gt;This is a security check to prevent attackers from replacing the actual payload with a malicious one with the intention of corrupting our API.&lt;/p&gt;

&lt;p&gt;Because we know that we are receiving the actual payload from GitHub since we set everything up and triggered the push event, we know it's not an attacker's payload causing the &lt;code&gt;401&lt;/code&gt; error. Something else must be wrong with our setup.&lt;/p&gt;

&lt;p&gt;Let's take a look at the validation logic on our API. This can be found in the &lt;code&gt;server.js&lt;/code&gt; file, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Validate payload&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validatePayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request Body empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&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;sig&lt;/span&gt; &lt;span class="o"&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sigHeaderName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;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="nx"&gt;sigHashAlg&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;digest&lt;/span&gt; &lt;span class="o"&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;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&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;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="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="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;||&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;digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Request body digest (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) did not match &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sigHeaderName&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;sig&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validatePayload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple of variables being used here in our code that are worth checking out to confirm that they are referencing the appropriate values. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sigHeaderName&lt;/code&gt;: This represents the signature header name sent by GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sigHashAlg&lt;/code&gt;: This is the algorithm used for the encryption.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secret&lt;/code&gt;: This is the API secret set on our GitHub webhook.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values are set on lines 10-12 in the &lt;code&gt;server.js&lt;/code&gt; file. Lets take a look at these definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Hub-Signature-256&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;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&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;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;XXXXXX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After observing these values, have you been able to catch the error? If the secret is not the same as the one we set in our webhook form, the validation will always fail.&lt;/p&gt;

&lt;p&gt;GitHub does not allow you to view the API secret you set in your webhooks a second time — you can only reset it. This is why you need to make sure that you remember the value or store it somewhere secure.&lt;/p&gt;

&lt;p&gt;For this practice, we know we set the secret as a simple &lt;code&gt;1234ABCD&lt;/code&gt; string. In real-world applications, you want to set a more complicated secret and reference it from an environment variable in your code.&lt;/p&gt;

&lt;p&gt;Change the value of the &lt;code&gt;secret&lt;/code&gt; to the right value, save the file, and restart the server. Now trigger another webhook by pushing a new commit to your GitHub repository. You will see a webhook entry similar to the one below on your Hookdeck CLI session:&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%2Ftq1i26jak3unqefmb2hc.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%2Ftq1i26jak3unqefmb2hc.png" alt="201 - CLI" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we have a successful operation on our API. Load the event page, which should display a page similar to the one below:&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%2F3dqc2a3qdn815i2cf9xd.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%2F3dqc2a3qdn815i2cf9xd.png" alt="201 event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on the green status badge below the &lt;strong&gt;Attempts&lt;/strong&gt; section to show the server response. The server returns a successful message, as shown below:&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%2F0jqzozqec61tyjygy7zs.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%2F0jqzozqec61tyjygy7zs.png" alt="201 server response" width="428" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, when you visit the endpoint &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;, you will see a collection like the one below:&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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F984fe387-4ff9-4765-b364-dc8249e06da3%2Fwebhook-logged.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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F984fe387-4ff9-4765-b364-dc8249e06da3%2Fwebhook-logged.png" alt="webhook-logged.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting GitHub webhook "invalid HTTP response 400" error
&lt;/h3&gt;

&lt;p&gt;Another error that can occur with GitHub webhooks is a &lt;code&gt;400&lt;/code&gt; HTTP error, which indicates a bad request. One thing to note when working with GitHub webhooks is that GitHub is your client. Your application, or whichever system (SaaS apps, CI/CD server, etc.) that you're integrating GitHub webhooks with, is the server.&lt;/p&gt;

&lt;p&gt;Thus, when you see a bad request, it means that your server is not receiving what it expects from GitHub. &lt;/p&gt;

&lt;p&gt;The first thing you want to check is the format in which you're sending your webhook payload. GitHub allows you to set the &lt;code&gt;Content-type&lt;/code&gt; to either &lt;code&gt;application/json&lt;/code&gt; or &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; . &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0wu0pplywf1nflcue2e.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%2Fl0wu0pplywf1nflcue2e.png" alt="Content Type" width="376" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure that the value you configured in your webhook form matches the content type your server is expecting.&lt;/p&gt;

&lt;p&gt;Sometimes, &lt;code&gt;400&lt;/code&gt; errors arise from empty payloads being sent by GitHub. If your server expects a payload to be sent, make sure that the &lt;code&gt;Active&lt;/code&gt; option on your GitHub webhook form is checked.&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%2Fmxal2cxiqutslbfzvy2z.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%2Fmxal2cxiqutslbfzvy2z.png" alt="Active toggle" width="462" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Just like any other architectural component of an application, webhooks run into errors. Debugging is considered part of the workflow when developing applications or setting up application infrastructure. As errors are inevitable, knowing what to look out for when a certain error occurs and having the right tools to debug the situation eases the pains experienced in troubleshooting. &lt;/p&gt;

&lt;p&gt;You also save time and push out solutions faster!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>github</category>
      <category>webhooks</category>
      <category>showdev</category>
    </item>
    <item>
      <title>GitHub Webhooks Tutorial</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 21 Oct 2021 18:44:27 +0000</pubDate>
      <link>https://forem.com/hookdeck/github-webhooks-tutorial-43lp</link>
      <guid>https://forem.com/hookdeck/github-webhooks-tutorial-43lp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As one of the biggest code hosting platforms available today, GitHub provides numerous ways for developers to integrate with the platform. One of those integration options is GitHub webhooks.&lt;/p&gt;

&lt;p&gt;In this article, we are going to explore GitHub webhooks. We will look at what a GitHub webhook is, and how to set it up on our GitHub account. Further, we will explore a sample API that receives and logs webhook events. This API will run on our local development environment, thus we will be receiving the webhooks on our local machine. However, if you have a local API you would rather use, feel free to follow along with the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is GitHub Webhooks?
&lt;/h2&gt;

&lt;p&gt;GitHub Webhooks gives developers the ability to integrate with the GitHub platform.&lt;/p&gt;

&lt;p&gt;Developers can subscribe to events taking place on GitHub, such as push events that occur when code is pushed to a repository, and respond to the event with an action.&lt;/p&gt;

&lt;p&gt;The response action can be anything: making a database entry in an external application, sending an email, or triggering a deployment process, just to name a few. Actions you can perform in response to events on GitHub are limited only by your imagination.&lt;/p&gt;

&lt;p&gt;When a subscribed event occurs on GitHub, GitHub triggers an HTTP POST request to the defined destination. This destination is specified as an HTTP URL, which is the endpoint to be called whenever the event occurs. This is known as the webhook URL. The endpoint handler at the webhook URL handles the action to be performed in response to the event.&lt;/p&gt;

&lt;p&gt;Along with the request, GitHub also sends the payload of data about the event that triggered the webhook. Information contained in this payload can be used for the action on the endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: How to set up GitHub Webhooks
&lt;/h2&gt;

&lt;p&gt;To begin the tutorial, let's take a look at the steps involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the sample Node.js API for receiving GitHub webhooks on your development machine&lt;/li&gt;
&lt;li&gt;Generate a webhook URL using the Hookdeck CLI&lt;/li&gt;
&lt;li&gt;Register for a webhook on GitHub&lt;/li&gt;
&lt;li&gt;Receive and inspect GitHub webhooks locally&lt;/li&gt;
&lt;li&gt;Make some commits and view logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Clone a demo API server for receiving GitHub webhooks
&lt;/h3&gt;

&lt;p&gt;As described earlier, GitHub webhooks need to be sent to an endpoint on your API.&lt;/p&gt;

&lt;p&gt;If you already have a server running with a &lt;code&gt;POST&lt;/code&gt; endpoint to receive the webhook request, you can skip this section. All you need to do is to take note of the port that your local API is running and also the endpoint to receive webhooks. Substitute these values as you follow this exercise.&lt;/p&gt;

&lt;p&gt;If you do not have a local API, you can use a &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; sample API available on the &lt;a href="https://github.com/hookdeck/nodejs-webhook-server-example" rel="noopener noreferrer"&gt;Hookdeck GitHub repository&lt;/a&gt;. Follow the instructions below to set it up.&lt;/p&gt;

&lt;p&gt;Clone the repository by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; github-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;cd &lt;/span&gt;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, you can then run the Node.js server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-github-webhook&lt;/code&gt;: This is the endpoint that will be receiving the GitHub webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Get the webhook URL for GitHub
&lt;/h3&gt;

&lt;p&gt;Endpoints on locally running servers cannot receive GitHub webhooks; a publicly accessible endpoint is required and this is where the Hookdeck CLI comes in. The &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; is built specifically for working with webhooks, and helps tunnel external HTTP requests into your local development environment by providing you with a publicly accessible HTTPS URL. You can also inspect the headers and payloads of your webhooks, as you will see later on in this tutorial.&lt;/p&gt;

&lt;p&gt;Visit Hookdeck's &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;CLI documentation&lt;/a&gt; to install and set up the tool on your operating system. For macOS users, you can run the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck/hookdeck/hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using the Windows operating system, use the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the setup process is complete, the next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port &lt;code&gt;1337&lt;/code&gt; value with the port number on which your API is running if you're not using the sample project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;strong&gt;&lt;em&gt;Create new source&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-github-webhook&lt;/em&gt;&lt;/strong&gt; (If you're using your own custom server, replace this value with your endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;My Github Response Server&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2F0g0r04dn23mj2i3tyhso.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%2F0g0r04dn23mj2i3tyhso.png" alt="CLI Ready" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up webhook on GitHub
&lt;/h3&gt;

&lt;p&gt;Next, you will need to subscribe to a GitHub webhook. Navigate to your repository of choice (this should be a repository you own) and go to &lt;strong&gt;Settings → Webhooks&lt;/strong&gt;. On the &lt;strong&gt;Webhooks&lt;/strong&gt; page, click on the &lt;code&gt;Add webhook&lt;/code&gt; button on the top right-hand corner.&lt;/p&gt;

&lt;p&gt;On the webhook form displayed, paste the webhook URL generated by the Hookdeck CLI into the &lt;code&gt;Payload URL&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Fill the remaining fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content type:&lt;/strong&gt; Select &lt;code&gt;application/json&lt;/code&gt; so that you can receive the payload as a JSON object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret:&lt;/strong&gt; You can leave this blank, but for the purpose of this tutorial enter &lt;code&gt;1234ABCD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL verification:&lt;/strong&gt; Leave this as the default option of &lt;code&gt;Enable SSL verification&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which events would you like to trigger this webhook?&lt;/strong&gt; This is the point where you subscribe for a GitHub event on your repository. For this tutorial, select the &lt;code&gt;Just the push event&lt;/code&gt; option, as we are only subscribing to the &lt;code&gt;push&lt;/code&gt; event. You can either subscribe for all events or a subset of the events using the other two options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active:&lt;/strong&gt; Leave this checked to receive event details when the GitHub webhook is triggered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the complete webhook form below:&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%2Fe1zigz6cuwftmx7t4nww.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%2Fe1zigz6cuwftmx7t4nww.png" alt="Add Webhook" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Add webhook&lt;/code&gt; button to complete the process.&lt;/p&gt;

&lt;p&gt;With this setup, any time you push code to your repository, a webhook request will be fired to the specified webhook URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Receive and inspect GitHub webhooks and payload
&lt;/h3&gt;

&lt;p&gt;Immediately after you complete registration for a webhook in the step above, a &lt;code&gt;ping&lt;/code&gt; webhook request will be fired to your webhook URL. Because we are using the Hookdeck CLI, we would already see the &lt;code&gt;ping&lt;/code&gt; webhook request logged on the terminal where the Hookdeck CLI is running, as shown below:&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%2Fbicm1na0m86po3kz1nyt.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%2Fbicm1na0m86po3kz1nyt.png" alt="CLI Webhook Entry" width="800" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see that we have received a &lt;code&gt;POST&lt;/code&gt; request with a &lt;code&gt;201&lt;/code&gt; status code indicating that a new entity was created on our server; in other words, a webhook object was logged. &lt;/p&gt;

&lt;p&gt;To view details of the webhook request, its headers, and payload, copy the Hookdeck dashboard URL printed along with your webhook request details on the CLI and load it in your browser.&lt;/p&gt;

&lt;p&gt;You will see a page similar to the one below:&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%2Fp8go7brms28ldfrxaeiq.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%2Fp8go7brms28ldfrxaeiq.png" alt="Event Page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On this page, you can go through and inspect all the data that came with your webhook request. For example, the &lt;code&gt;Headers&lt;/code&gt; section is expanded in the image below:&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%2F73hny5yxqw7b82aq7xc6.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%2F73hny5yxqw7b82aq7xc6.png" alt="Event Headers" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also view all that is contained in the webhook request payload by expanding the &lt;code&gt;Body&lt;/code&gt; section as shown below:&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%2Fudxunnadscfd0hcl1y6f.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%2Fudxunnadscfd0hcl1y6f.png" alt="Event Body" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this setup, you get full and clear visibility into your webhook requests and you will be able to monitor and quickly troubleshoot your webhooks when any failure occurs.&lt;/p&gt;

&lt;h3&gt;
  
  
  View Application webhook logs
&lt;/h3&gt;

&lt;p&gt;To check that the webhook information is being logged, make a few commits to your GitHub project. You should see webhook event entries in the terminal where your Hookdeck CLI session is running. Now visit the endpoint &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;, where you should see a couple of log entries similar to the image below:&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%2Flbbpp1tmoebq98odwkds.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%2Flbbpp1tmoebq98odwkds.png" alt="Webhook Logs" width="780" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, we are logging the &lt;code&gt;repo&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, and &lt;code&gt;timestamp&lt;/code&gt; of the commit. Ignore the first entry as this is simply a test entry so that the application database does not start empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub webhooks verification
&lt;/h2&gt;

&lt;p&gt;A publicly accessible endpoint, just like the one being used as your webhook URL, can receive requests from anyone. This situation makes your endpoint vulnerable, as ill-intentioned individuals or bots can easily spoof requests imitating GitHub to dump irrelevant data into your system or manipulate your application.&lt;/p&gt;

&lt;p&gt;You should verify the requests hitting your webhook URL to ascertain that they are from GitHub. GitHub helps you achieve this using the token secret we set in the form in an earlier step.&lt;/p&gt;

&lt;p&gt;GitHub uses this token to create a hash signature with each payload. This hash signature is included with the headers of each request as &lt;code&gt;X-Hub-Signature-256&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this signature, you can validate your payloads. GitHub uses the &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC&lt;/a&gt; algorithm to compute the hash and it is the same algorithm you will use to implement the validation on your server. &lt;/p&gt;

&lt;p&gt;At the moment, our project is not enforcing this check but the code can be found commented out as shown below:&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="c1"&gt;//Validate payload&lt;/span&gt;
&lt;span class="cm"&gt;/* function validatePayload(req, res, next) {

    if(req.method == "POST"){
        if (!req.rawBody) {
            return next('Request body empty')
        }

        const sig = Buffer.from(req.get(sigHeaderName) || '', 'utf8')
        const hmac = crypto.createHmac(sigHashAlg, secret)
        const digest = Buffer.from(sigHashAlg + '=' + hmac.update(req.rawBody).digest('hex'), 'utf8');

        if (sig.length !== digest.length || !crypto.timingSafeEqual(digest, sig)) {
            return next(`Request body digest (${digest}) did not match ${sigHeaderName} (${sig})`)
        }
    }

    return next()

}
app.use(validatePayload); */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From lines 10 to 12, you can see the validation variables defined, as shown below:&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;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Hub-Signature-256&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;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&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;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;1234ABCD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;secret&lt;/code&gt; variable is the key we set when creating our webhook. In a production environment, these values should be kept in environment variables.&lt;/p&gt;

&lt;p&gt;You can uncomment the payload validation middleware to ensure that your webhook payload is validated against the API secret set.&lt;/p&gt;

&lt;p&gt;For more information on securing your GitHub webhooks, you can check out the GitHub documentation &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I disable a GitHub Webhook?
&lt;/h2&gt;

&lt;p&gt;At the moment, there is no setting on a GitHub webhook configuration that disables a webhook. Some will suggest that you uncheck the &lt;code&gt;Active&lt;/code&gt; parameter but this only stops the delivery of the webhook payload, it may not prevent the event itself from being fired.&lt;/p&gt;

&lt;p&gt;The only way you can be sure you have disabled a webhook is by deleting it completely from your GitHub &lt;strong&gt;Webhooks&lt;/strong&gt; page. This may seem a bit extreme if you only want to disable it temporarily.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://hookdeck.com/" rel="noopener noreferrer"&gt;Hookdeck&lt;/a&gt;, you can use the &lt;code&gt;Pause&lt;/code&gt; feature on the dashboard to temporarily stop your webhooks from being delivered. When you're ready to start receiving them once again, you can unpause the webhook.&lt;/p&gt;

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

&lt;p&gt;Webhooks are a fantastic offering by GitHub to help developers extend their integrations with the platform to more custom use cases. This enables the development of more robust applications that take advantage of events occurring on GitHub.&lt;/p&gt;

&lt;p&gt;In this tutorial, you have learned how to set up and receive GitHub webhooks on your API endpoints. The tasks you can perform in response to these events are only limited by your imagination.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>github</category>
      <category>webhook</category>
    </item>
    <item>
      <title>Top 5 Use Cases for GitHub Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Wed, 06 Oct 2021 20:28:09 +0000</pubDate>
      <link>https://forem.com/hookdeck/top-5-use-cases-for-github-webhooks-284i</link>
      <guid>https://forem.com/hookdeck/top-5-use-cases-for-github-webhooks-284i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In our previous article, we looked at how to get started using GitHub webhooks.&lt;/p&gt;

&lt;p&gt;However, what are the actual real-world applications of GitHub webhooks? In what ways can this technology deliver value to our applications? These are the questions we will be trying to provide answers to in this article by taking a look at some of the most common real-world applications of GitHub webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are GitHub webhooks important/useful?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the various use cases of GitHub webhooks, let's discuss why GitHub webhooks are a useful piece of technology. For over a decade now, GitHub has served as a central hub for storing, sharing, and collaborating on code-based projects.&lt;/p&gt;

&lt;p&gt;It can be argued that GitHub houses the code for most of the software applications, frameworks, plugins, and any code-based technology that we use today. Thus, it is important that such a platform should open doors to integrations with other external systems that rely on the code hosted on it.&lt;/p&gt;

&lt;p&gt;GitHub webhooks provides one such integration opportunity by allowing external applications to subscribe for events taking place on GitHub. These external applications can then receive notifications and information about their subscribed events, and trigger workflows in response to these events. This way, subscribed applications can deliver more value to their users (especially through the automation of tasks). The beauty of GitHub webhooks is that they are not restricted to any use case; you are only limited by your own imagination. &lt;/p&gt;

&lt;p&gt;In the following sections, we will take a look at some of the most common scenarios in which application engineers have employed the use of GitHub webhooks to give you an idea of the flexibility that is possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triggering continuous integration build pipelines
&lt;/h2&gt;

&lt;p&gt;One of the most common applications of GitHub webhooks is the triggering of continuous integration pipelines to run tests on code being pushed to a branch. Team members working on a project send pull requests from feature branches to the main branch. Before these feature branches are merged to the main branch, it's considered best practice to run the code to be merged through tests.&lt;/p&gt;

&lt;p&gt;You can set up a continuous integration (CI) pipeline on a CI server (&lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;, &lt;a href="https://circleci.com/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;, etc.). Then subscribe for &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pull_request&lt;/code&gt; events on GitHub to fire webhooks to trigger your CI server to run tests on the branch. &lt;/p&gt;

&lt;p&gt;This will prevent defective code from being merged to your main branch when errors are detected in the CI build and the build fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically deploying applications to destination servers
&lt;/h2&gt;

&lt;p&gt;For web projects, it is important for development teams to deliver their code to application servers where users and/or testers can access it. Whether you need to deliver your web app to a staging or production environment, a continuous delivery pipeline can be set up to move your code to the hosting environment.&lt;/p&gt;

&lt;p&gt;However, this deployment pipeline needs a trigger to tell it when to ship your code to the host. Developers can simply subscribe for a &lt;code&gt;push&lt;/code&gt; event on the main branch (for deployment to production servers), or any deployment branch. When the &lt;code&gt;push&lt;/code&gt; event occurs, a webhook is sent to trigger the deployment pipeline to ship the code to the hosting environment.&lt;/p&gt;

&lt;p&gt;CI/CD platforms like CircleCI have API endpoints to trigger a deployment pipeline. You can simply point your webhook to this endpoint to trigger a deployment to your servers. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sending notifications to team collaboration channels like Slack and Discord
&lt;/h2&gt;

&lt;p&gt;GitHub tries its best to send email notifications for commits and pull requests on repositories. However, development teams spend most of their time on collaboration platforms like &lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; and &lt;a href="https://discord.com/" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thus, the most effective way to communicate activities happening on code repositories to team members, especially code reviewers and deployment approvers, is via these collaboration channels. This ensures that anyone who needs to take immediate action gets the notification on time. This can also help facilitate discussion on commits and issues, which allows the team to stay in sync with activities taking place on the repository.&lt;/p&gt;

&lt;p&gt;Discord allows developers to generate a webhook endpoint URL that can be submitted to GitHub when subscribing to a webhook. This allows you to get instant notifications on subscribed events in your Discord channels. You can follow this &lt;a href="https://www.notion.so/5b1761213e33fc5b54ec7f6379034a22" rel="noopener noreferrer"&gt;GitHub gist&lt;/a&gt; to see how GitHub webhooks can be used to channel event details to Discord channels.&lt;/p&gt;

&lt;p&gt;When subscribing for webhooks to be sent to discussion channels, you can subscribe to as many events as you're interested in. There are no rules as to what you should have sent to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating external issue trackers
&lt;/h2&gt;

&lt;p&gt;GitHub includes an &lt;strong&gt;Issues&lt;/strong&gt; tab on each repository where users of the application and code collaborators can submit issues observed. Discussion threads can then be created on each issue to share information about resolving the issue.&lt;/p&gt;

&lt;p&gt;However, some external applications do a better job at handling issues than GitHub. These issue trackers often possess more features for dealing with code issues and helping developers collaborate better on a project. &lt;/p&gt;

&lt;p&gt;Some of these issue trackers, like &lt;a href="https://www.atlassian.com/software/jira" rel="noopener noreferrer"&gt;Jira&lt;/a&gt;, have built-in support for GitHub to follow up on issues on a repository. Others, like &lt;a href="https://www.pivotaltracker.com/help/articles/github_integration/" rel="noopener noreferrer"&gt;Pivotal Tracker&lt;/a&gt; and &lt;a href="https://docs.mongodb.com/realm/tutorial/backend/" rel="noopener noreferrer"&gt;MongoDB Realm&lt;/a&gt;, provide you with a webhook URL that you can add to GitHub to subscribe to the &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types#issuesevent" rel="noopener noreferrer"&gt;issues events&lt;/a&gt;, &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types#issuecommentevent" rel="noopener noreferrer"&gt;issue comment event&lt;/a&gt;, &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types#commitcommentevent" rel="noopener noreferrer"&gt;commit comment event&lt;/a&gt;, and any other event related to issues tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating workflows on external processes
&lt;/h2&gt;

&lt;p&gt;Another interesting use of GitHub webhooks is in triggering workflows on external applications and processes. This use case is custom, and always dependent on what the application engineer wants to achieve. &lt;/p&gt;

&lt;p&gt;It can be as simple as logging activities taking place on GitHub for audit purposes, or triggering database servers to backup data before a release is made. Project management applications can subscribe to events like &lt;code&gt;issues&lt;/code&gt; and &lt;code&gt;pull_request&lt;/code&gt; to keep track of the project.&lt;/p&gt;

&lt;p&gt;Customer support applications can subscribe to the &lt;code&gt;issues&lt;/code&gt; event to collate complaints, comments, and issues that users are reporting on GitHub.&lt;/p&gt;

&lt;p&gt;If there is a process you need to automate in your custom application, database servers, &lt;a href="https://en.wikipedia.org/wiki/Software_as_a_service" rel="noopener noreferrer"&gt;SaaS&lt;/a&gt; applications, or email servers in response to any activity on GitHub, you can achieve this automation with GitHub webhooks.&lt;/p&gt;

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

&lt;p&gt;The simplicity of GitHub makes it easy to integrate into a wide range of external applications and processes. Almost every application supports HTTP, and since webhook requests are simple HTTP requests carrying a payload of event information, they can easily communicate with a lot of online applications. In this article, we have looked at some of the common scenarios where GitHub webhooks are deployed. &lt;/p&gt;

&lt;p&gt;GitHub webhooks use cases are not limited to what we have seen in this article, but having these examples at hand will help you better understand how to use GitHub webhooks to their full potential.&lt;/p&gt;

&lt;p&gt;If you are interested in building an integration with GitHub webhooks, we recommend reading our tutorial for GitHub webhooks to kick start your project! &lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>github</category>
      <category>webhooks</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with GitHub Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Wed, 29 Sep 2021 16:23:06 +0000</pubDate>
      <link>https://forem.com/hookdeck/getting-started-with-github-webhooks-4a9c</link>
      <guid>https://forem.com/hookdeck/getting-started-with-github-webhooks-4a9c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; is arguably the largest remote code hosting service in the world. It can also be argued that it is the most-used platform by developers around the world for working on both personal and team code-based projects. One of the attributes contributing to GitHub's widespread adoption is the ability to integrate the platform into development workflows, OAuth applications, CI/CD processes, and many more use cases.&lt;/p&gt;

&lt;p&gt;In this article, we take a look at one of the strategies for integrating GitHub into external workflows and applications, known as GitHub webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are GitHub webhooks?
&lt;/h2&gt;

&lt;p&gt;Just as webhooks are being provided by many &lt;a href="https://en.wikipedia.org/wiki/Software_as_a_service" rel="noopener noreferrer"&gt;SaaS&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Platform_as_a_service" rel="noopener noreferrer"&gt;PaaS&lt;/a&gt; systems for the purpose of integration, GitHub webhooks give developers the ability to integrate with the GitHub platform.&lt;/p&gt;

&lt;p&gt;Developers can subscribe to events taking place on GitHub, for example Push events that occur when code is pushed to a repository, and respond to the event with an action.&lt;/p&gt;

&lt;p&gt;The response action can be anything, such as making a database entry in an external application, sending an email, triggering a deployment process, and more. Actions you can perform in response to events on GitHub are only limited by your imagination.&lt;/p&gt;

&lt;p&gt;When a subscribed event occurs on GitHub, GitHub triggers an HTTP POST request to the defined destination. This destination is specified as an HTTP URL, which is the endpoint that is to be called whenever the event occurs. This is known as the webhook URL. The endpoint handler at the webhook URL handles the action to be performed in response to the event.&lt;/p&gt;

&lt;p&gt;GitHub also sends the payload of data about the event that triggered the webhook along with the request. Information contained in this payload can be used for action to be taken on the endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the differences betweem GitHub webhooks and actions ?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; is a built-in CI/CD tool for GitHub and allows developers to automate the testing and deployment of code. When new code is pushed to a GitHub repository, GitHub actions can be set up to test the code, merge it to the main repository when tests pass and also deploy the application to a destination server.&lt;/p&gt;

&lt;p&gt;GitHub webhooks, on the other hand, are just plain subscriptions to events that take place on the repository and can be used to trigger different actions asides from CI/CD workflows.&lt;/p&gt;

&lt;p&gt;You can use GitHub webhooks to achieve the same CI/CD operations found in GitHub Actions, however, GitHub is not responsible for the CI/CD operation. You will need to connect your CI/CD triggers to the necessary event in your repository with GitHub webhooks and use an external CI/CD server.&lt;/p&gt;

&lt;p&gt;GitHub actions also require a configuration script to set up, whereas GitHub webhooks do not require a script.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use webhooks on GitHub?
&lt;/h2&gt;

&lt;p&gt;Webhooks can be configured on a &lt;a href="https://docs.github.com/en/rest/reference/orgs#webhooks" rel="noopener noreferrer"&gt;GitHub organization account&lt;/a&gt;, a &lt;a href="https://docs.github.com/en/rest/reference/repos#hooks" rel="noopener noreferrer"&gt;specific repository&lt;/a&gt;, or a &lt;a href="https://docs.github.com/en/rest/reference/apps" rel="noopener noreferrer"&gt;GitHub App&lt;/a&gt;. A webhook is connected to a GitHub event type and you can configure webhooks for more than one event. Once set up, webhooks will be sent each time a subscribed event is triggered.&lt;/p&gt;

&lt;p&gt;Here are the steps to set up a webhook on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the repository you want to subscribe to events on&lt;/li&gt;
&lt;li&gt;Go to Settings → Webhooks&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Add webhook&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the webhook form specifying your webhook URL&lt;/li&gt;
&lt;li&gt;Subscribe to all, or only the events you're interested in&lt;/li&gt;
&lt;li&gt;Click the &lt;code&gt;Add webhook&lt;/code&gt; button to complete the process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See below an example of a webhook setup on GitHub.&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%2Fsx8xuag6y1w248n3pxgv.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%2Fsx8xuag6y1w248n3pxgv.png" alt="Add GitHub Webhook" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Immediately after the webhook is created, GitHub sends a &lt;code&gt;ping&lt;/code&gt; webhook to your endpoint. This is to test that your endpoint is live, and you can also use it to verify the expected reaction on the webhook URL.&lt;/p&gt;

&lt;p&gt;You can create up to 20 webhooks for each organization, repository, or GitHub app you're interested in.&lt;/p&gt;

&lt;p&gt;Webhooks can also be configured via the GitHub API through authenticated sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of GitHub webhooks events
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, each webhook is tied to an event that takes place on GitHub, so let's look at the types of events that GitHub supports. GitHub supports over a dozen event types but except in very peculiar cases, your interest will be focused on a few common ones.&lt;/p&gt;

&lt;p&gt;Some of the common events supported by GitHub are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Push Event:&lt;/strong&gt; This event is triggered when one or more commits are pushed to a repository branch or tag. This is one of the most common events developers subscribe to, as a lot can be done in response to a Push Event. You can trigger tests in continuous integration pipelines, send alerts to code reviewers on your team to review the new commit, and even trigger deployments to staging and/or production servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull Request Event:&lt;/strong&gt; This event is triggered for any activity that involves Pull Requests, for example when a Pull Request is opened, assigned, labeled, or closed, etc. The type of activity is specified in the &lt;code&gt;action&lt;/code&gt; property of the webhook request payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues Event:&lt;/strong&gt; Any activity related to issues causes this event to be triggered. Just like the &lt;code&gt;Pull Request Event&lt;/code&gt;, the type of activity is specified in the &lt;code&gt;action&lt;/code&gt; property of the payload. The issue itself is contained in the &lt;code&gt;issue&lt;/code&gt; property. One common use case of this event is in updating an external issue tracker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Event:&lt;/strong&gt; This event is fired when a repository user creates a new branch or tag in the remote repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a full list of events supported by GitHub, their description and payload properties, check out the documentation &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Familiarity with the supported events will help you get the best out of GitHub webhooks.&lt;/p&gt;

&lt;p&gt;While subscribing for events on a repository, you might be tempted to just subscribe for every single event available. This is not advisable, as your GitHub webhooks rate limits can easily be exceeded. Your endpoint will also be burdened with a large number of irrelevant events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding information on the GitHub webhooks documentation
&lt;/h2&gt;

&lt;p&gt;GitHub has well-documented and exhaustive material on webhooks. However, sometimes you just need to get started quickly and you want to navigate to the information you need as fast as possible.&lt;/p&gt;

&lt;p&gt;Below is a list of the most common pages you will be interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/creating-webhooks" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types" rel="noopener noreferrer"&gt;GitHub Webhooks Event Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/rest/reference/orgs#webhooks" rel="noopener noreferrer"&gt;Setting up webhooks on GitHub Organizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/rest/reference/apps#webhooks" rel="noopener noreferrer"&gt;Setting up webhooks on GitHub Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/testing-webhooks" rel="noopener noreferrer"&gt;Testing Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks" rel="noopener noreferrer"&gt;Securing Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;GitHub webhooks further expand the range of integration that developers have with the platform. Also, they are easy to set up and highly reliable. With GitHub webhooks, you can subscribe to events that trigger workflows in your external applications, like your custom application servers, Zapier, G Suite, CI/CD servers, etc.&lt;/p&gt;

&lt;p&gt;In this article, we dipped our toes in the water that is GitHub webhooks. In subsequent articles, we are going to be taking a closer look at using GitHub webhooks, securing them, and best practices you should be aware of.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>github</category>
      <category>showdev</category>
      <category>tutorial</category>
      <category>webhooks</category>
    </item>
  </channel>
</rss>
