<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Convoy</title>
    <description>The latest articles on DEV Community by Convoy (@convoy).</description>
    <link>https://dev.to/convoy</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%2F5411%2Fb0dbcdbf-1e1a-4483-a5fa-64df21e39722.png</url>
      <title>DEV Community: Convoy</title>
      <link>https://dev.to/convoy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/convoy"/>
    <language>en</language>
    <item>
      <title>Why Microservices Need Webhook Gateways</title>
      <dc:creator>Lotanna Nwose</dc:creator>
      <pubDate>Wed, 21 Jun 2023 14:16:35 +0000</pubDate>
      <link>https://dev.to/convoy/why-microservices-need-webhook-gateways-18j0</link>
      <guid>https://dev.to/convoy/why-microservices-need-webhook-gateways-18j0</guid>
      <description>&lt;p&gt;A Webhook Gateway serves as a forward and reverses proxy to send and receive webhooks to client endpoints and from third-party providers. They are designed to enforce security, resilience and high availability on all webhook events for an organisation. We wrote a more detailed description &lt;a href="https://getconvoy.io/blog/what-are-webhook-gateways"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uNnxziNb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ul6ck43bltkcaobqk26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uNnxziNb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ul6ck43bltkcaobqk26.png" alt="A schematic diagram showing the flow of events in both directions." width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image above describes the flow of webhook events from the microservices to client endpoints as well as from third parties to the microservices. In this article, we will discuss why webhooks gateways are relevant to the microservices architecture, and what common features are found in the webhooks gateway. Finally, we will discuss some of the caveats to bear in mind while adopting a webhooks gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need Webhook Gateways?
&lt;/h2&gt;

&lt;p&gt;The various advantages of a microservice architecture subsequently present unique challenges that Webhook Gateways are purpose-built to address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Centralised Infrastructure to Decentralised Microservices
&lt;/h3&gt;

&lt;p&gt;Microservice architectures split up several functions of an application into separate services, that are owned by different teams. This separation is typically based on a common domain e.g. Payments service, Notifications service etc. This design pattern engenders each team to build, deploy and maintain their services independently. Unlike a monolith application, teams are able to increase velocity and deliver features faster. This approach means increased complexity for webhook management: How are we able to route webhook events from third parties to the right microservice(s)? How are we able to route webhook events from backend services to client endpoints?&lt;/p&gt;

&lt;p&gt;This is where a webhooks gateway comes in. They’re able to provide a central infrastructure capable of ingesting, and routing webhooks both internally to backend services and externally to client endpoints. This central platform also reduces the learning curve for new teams to add webhooks to their services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Microservice Language and Protocol Agnostic
&lt;/h3&gt;

&lt;p&gt;One of the major benefits of the microservice approach is the ability for teams to utilise the best tools for the job. For example, one team can decide to build their API using Rails, GraphQL and MongoDB, while another team can decide to build their API using Golang, gRPC and PostgreSQL. &lt;/p&gt;

&lt;p&gt;Regardless of the technology stack chosen by each team. Webhook gateways rely on a consistent payload structure to send to client endpoints and an endpoint to receive from a third-party. As long as the payload format and structure remain consistent, developers can implement the business logic with any technology stack they prefer. While leaving the gateway to cross-cutting concerns like retries, rate limiting and signing the payload.&lt;/p&gt;

&lt;p&gt;Services can be rewritten from Python to Rust and redeployed with the Gateway. To the client endpoints, the payload remains the same, and it can continue to rely on the payload format and structure to remain the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing to Microservices based on payload structure
&lt;/h3&gt;

&lt;p&gt;When webhooks are ingested into your systems from third-party providers, in a monolith all events are routed to the same application all the time. However, in a microservice architecture, one or more microservice(s) can rely on the event from the provider to carry out separate actions. &lt;/p&gt;

&lt;p&gt;Webhook Gateways can be configured to receive events and route them to one or more destinations. They can match both headers and payload to determine webhook's destination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traffic Control to prevent overloading of resources
&lt;/h3&gt;

&lt;p&gt;Internet-facing services are common targets for malicious attacks and exploitation. They need to handle a sudden spike in requests from valid users and bad actors. This requires features like request throttling and blacklist capabilities to keep systems reliable and secure.&lt;/p&gt;

&lt;p&gt;Webhook Gateways are an effective barrier against Distributed Denial of Service (DDoS) attacks—throttling the number of requests made to affected services, so the service isn’t overwhelmed and protecting it against becoming unresponsive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Features &amp;amp; Benefits
&lt;/h2&gt;

&lt;p&gt;A Webhook Gateway is located at the outer edge of your microservices and acts as a proxy to manage all ingress and egress webhook traffic. The Webhook Gateway handles several features:&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Webhook Events sent to client endpoints need to be securely delivered to client endpoints. Clients need to validate that the message has not been tampered with in transit, and they need to know that events are not being re-transmitted by malicious actors, in some sensitive environments they need to authenticate the webhook provider and authorise their IP address through their network firewall.&lt;/p&gt;

&lt;p&gt;When receiving events from third-party providers (e.g. Stripe, Twilio ), consumers need to verify the webhooks before routing them to backend services for processing&lt;/p&gt;

&lt;p&gt;Webhooks Gateway is able to centralise cross-cutting concerns like webhooks security and securely receive events from backend services, sign the payload, timestamp the payload, and deliver them reliably with a static IP address. Since they exist at the outer edge of your microservices they are able to prevent exposing any of your backend services directly to the internet and reduce the attack surface area for malicious actors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilience
&lt;/h3&gt;

&lt;p&gt;Managing HTTP Endpoints is one of the core responsibilities of a Webhook Gateway. Standard webhooks flow is a post request to an endpoint, and expect a 2xx status code in return, indicating success. However, these endpoints fail from time to time for several reasons: flaky networks, spikes in traffic, code or infrastructure changes that resulted in downtime, and degraded performance in the client’s application dependencies.&lt;/p&gt;

&lt;p&gt;Due to the various types of issues that can prevent reliable webhooks delivery, webhook Gateways add important features like timeouts; to prevent waiting endlessly for responses, and retries; to automatically re-transmit webhook events, rate limiting; to deliver events to client endpoints at a steady pace the client can process, and circuit breaking; to prevent wasting system resources on zombie endpoints (i.e. endpoints that have failed consecutively over time)&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer-Facing Dashboard
&lt;/h3&gt;

&lt;p&gt;Debugging webhooks delivery is an important aspect of the responsibilities of webhooks providers to clients. Best practice enables engineers to debug webhook delivery without human interaction with the engineering team of the provider.&lt;/p&gt;

&lt;p&gt;Webhook Gateways provide such a utility to generate rich customer-facing webhooks dashboard where users can directly (zero human intervention) debug webhook delivery issues and fix failing endpoints with a few clicks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Data Source Options
&lt;/h3&gt;

&lt;p&gt;Webhook Gateways need to be flexible and versatile. Microservices emit events through various channels. For example, building on Google Cloud, developers will often use Google PubSub to emit happening in one or more microservices, similar to AWS for Amazon SQS, and other tools like Kafka and RabbitMQ. &lt;/p&gt;

&lt;p&gt;Plugging into all these options makes a webhook gateway such a versatile tool for streaming webhook events from multiple event-generating microservice to send the events to the client endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats of using Webhooks Gateways
&lt;/h2&gt;

&lt;p&gt;While there are several benefits to deploying a Webhooks gateway, there are some caveats to keep in mind when adopting a Webhooks gateway for your organisation, such as:&lt;/p&gt;

&lt;p&gt;The webhooks gateway is a new software component in your environment. It would require learning and building expertise in it. Platform Engineers and SREs will need to integrate, deploy and maintain this new software component and build production excellence running the webhooks gateway.&lt;/p&gt;

&lt;p&gt;By design, a webhooks gateway is responsible for webhooks traffic (inbound and outbound) of your organisation, This, therefore, becomes a single point of failure for the entire organisation. Webhooks gateway then needs to be designed for high availability to ensure no data loss for any webhook traffic. &lt;/p&gt;

&lt;p&gt;In the end, webhook gateways aren’t one-size fits all. You can build a webhook gateway in-house or acquire an off-the-shelf solution like Convoy while analysing your system's unique requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;A Webhook Gateway can enhance your microservice-based application with capabilities like secure webhook delivery with signatures, static IPs, resilience with retries, rate limiting, and customer-facing webhook dashboards. However, before adding a webhooks gateway to your application architecture, you need to understand what problems you’re trying to address, what events you’ll be sending and if the product you are choosing offers those features.&lt;/p&gt;

&lt;p&gt;Convoy is the fastest open-source webhooks gateway available today. With Convoy, you’ll be able to configure advanced event routing, incoming and outgoing webhooks, and customer-facing webhooks dashboard, and securely deliver events with payload signing and Static IPs.&lt;/p&gt;

&lt;p&gt;If you're exploring solutions for your webhooks, we welcome you to try Convoy Cloud for free today. &lt;/p&gt;

&lt;h2&gt;
  
  
  A little favour
&lt;/h2&gt;

&lt;p&gt;If you feel like this article helped you understand Webhooks better! I would be super happy if you could give us a star! And let me also know in the comments ❤️&lt;br&gt;
&lt;a href="https://github.com/frain-dev/convoy"&gt;https://github.com/frain-dev/convoy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aXX-BPFi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3k5was4p5oplousd5m9b.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aXX-BPFi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3k5was4p5oplousd5m9b.gif" alt="please meme Gif" width="478" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>5 Reasons Engineers do not use Webhook Gateways Yet</title>
      <dc:creator>Lotanna Nwose</dc:creator>
      <pubDate>Fri, 16 Jun 2023 11:14:49 +0000</pubDate>
      <link>https://dev.to/convoy/5-reasons-engineers-do-not-use-webhook-gateways-yet-3m53</link>
      <guid>https://dev.to/convoy/5-reasons-engineers-do-not-use-webhook-gateways-yet-3m53</guid>
      <description>&lt;p&gt;Speaking with platform engineers at events, in various online communities and even within the Convoy community validated the notion that managing webhooks in production is not always easy and can require a lot of effort. &lt;a href="https://getconvoy.io/blog/webhook-gateways-for-platform-engineers"&gt;Platform engineers&lt;/a&gt; believe strongly in &lt;a href="https://www.gartner.com/en/articles/what-is-platform-engineering"&gt;frictionless self-service developer experience&lt;/a&gt;, and solving for efficiency to quickly provide business value. Let’s take a look at why some haven't adopted a webhooks gateway such as &lt;a href="https://getconvoy.io"&gt;Convoy&lt;/a&gt; to manage webhook events and integrations yet and what we are doing to change that. &lt;/p&gt;

&lt;h3&gt;
  
  
  Security Concerns
&lt;/h3&gt;

&lt;p&gt;Webhooks can be vulnerable to attacks such as man-in-the-middle and replay attacks, which can compromise the security of the entire system. Some engineers worry that using a webhooks gateway could introduce additional security risks, particularly if the gateway is hosted by a third-party provider. &lt;/p&gt;

&lt;p&gt;Convoy provides advanced webhook security with endpoint authentication, payload signing, rolling webhook secrets, replay attack prevention, and forward-compatible scheme upgrades. For data security and compliance concerns, you can self-host Convoy using the Community Edition or Convoy Enterprise on-prem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Considerations
&lt;/h3&gt;

&lt;p&gt;We are currently in a bear market and so engineering teams are working towards capital efficiency so cost is another factor that is preventing engineers from adopting a webhooks gateway. Engineers worry that these costs will add up over time, particularly if they have multiple webhook integrations to manage.&lt;/p&gt;

&lt;p&gt;We have made that decision even easier at Convoy, making the product &lt;a href="https://getconvoy.io/blog/Convoy-Webhooks-is-free-for-developers"&gt;free forever for developers&lt;/a&gt; and then having the best &lt;a href="https://getconvoy.io/pricing"&gt;cost-effective pricing&lt;/a&gt; in the market today. Our commitment is to builders and we would continue to show that with our pricing strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Complexities
&lt;/h3&gt;

&lt;p&gt;Readiness to integrate is another interesting issue, some of the startup to mid-level teams we have spoken to are sometimes spent for time. Some engineers are not ready to integrate with a new tool just yet, others might be in the middle of a project or not have the bandwidth to take on something new. &lt;/p&gt;

&lt;p&gt;On the other hand, there is a general assumption that integrating new tools can take days and lots of effort. With Convoy, you can get up and running in a few minutes without even speaking to anyone. If you do need to, there is a &lt;a href="https://convoy-community.slack.com/join/shared_invite/zt-xiuuoj0m-yPp~ylfYMCV9s038QL0IUQ#/shared-invite/email"&gt;growing community of platform engineers&lt;/a&gt; including the Convoy engineers available to answer questions at any time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of Awareness
&lt;/h3&gt;

&lt;p&gt;Due to how relatively new the webhooks management space is, lots of platform engineers find Convoy from the content we put out, recommendations from their network and a small percentage from keyword searches. Also, just like any new space, lots of engineers are not aware of &lt;a href="https://getconvoy.io/blog/10-most-common-use-cases-of-a-webhook-gateway"&gt;benefits and features&lt;/a&gt; that webhook gateways have out-of-the-box to make their workflows more efficient. &lt;/p&gt;

&lt;p&gt;We are creating more valuable content around webhooks and breaking down its complexities, the more we do, the more you can find Convoy when you do a quick Google Search for anything webhooks related.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resistance to Change
&lt;/h3&gt;

&lt;p&gt;Many engineers are used to managing webhook integrations manually, and are hesitant to switch to a new platform or service. This is true, especially in large organisations where it takes months to go from one developer buy-in to team-wide adoption. This is the classic &lt;a href="https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=&amp;amp;cad=rja&amp;amp;uact=8&amp;amp;ved=2ahUKEwiKzO-av9n9AhUlolwKHYe7AmoQFnoECCgQAQ&amp;amp;url=https%3A%2F%2Fwww.forbes.com%2Fsites%2Fforbestechcouncil%2F2020%2F03%2F04%2Fbuild-vs-buy-why-most-businesses-should-buy-their-next-software-solution%2F&amp;amp;usg=AOvVaw3muvuN-zL7IsiGKZaogu9L"&gt;build or buy analogy&lt;/a&gt;, we do know that adopting a webhooks gateway can provide significant benefits, and significantly reduced debugging time exponentially as you scale.&lt;/p&gt;

&lt;p&gt;With Convoy you can manage both incoming and outgoing webhooks, API providers can start their change management process by updating their Docs and telling their customers to receive their webhooks with Convoy. Time and resources saved by customer success teams using this approach is usually a good motivation to buy later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Do you have any unmentioned reason why you have not tried out the Convoy webhooks gateway? Convoy is a reliable webhook gateway for sending and receiving millions of webhook events with robust support for Retries, Rate Limiting, Static IPs, Circuit Breaking, and Rolling Secrets. You can secure your payloads, scale horizontally and get endpoint failure alerts to debug faster with Convoy. You should try it out &lt;a href="https://github.com/frain-dev/convoy"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help me out!
&lt;/h2&gt;

&lt;p&gt;If you feel like this article helped you understand Webhooks better! I would be super happy if you could give us a star! And let me also know in the comments ❤️&lt;br&gt;
&lt;a href="https://github.com/frain-dev/convoy"&gt;https://github.com/frain-dev/convoy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--um2ubKnO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dc0nm6k9ysn9rfm636yd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--um2ubKnO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dc0nm6k9ysn9rfm636yd.gif" alt="Cat meme Gif" width="500" height="380"&gt;&lt;/a&gt;&lt;br&gt;
Thanks for reading!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>10 Common Ways Engineers Use Webhook Gateways</title>
      <dc:creator>Lotanna Nwose</dc:creator>
      <pubDate>Wed, 14 Jun 2023 16:05:31 +0000</pubDate>
      <link>https://dev.to/convoy/10-common-ways-engineers-use-webhook-gateways-45oj</link>
      <guid>https://dev.to/convoy/10-common-ways-engineers-use-webhook-gateways-45oj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A webhook gateway is a webhook management tool that sits between a webhook provider and the webhooks consumer. It acts as both a reverse and forward proxy for webhooks. It can be deployed by both the API provider that sends webhooks and the consumer that receives webhooks to handle webhook events multiplexing and de-multiplexing respectively.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--urZk8Hiw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ypnty1fq0tynmivgtbbd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--urZk8Hiw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ypnty1fq0tynmivgtbbd.gif" alt="get started gif" width="480" height="266"&gt;&lt;/a&gt;&lt;br&gt;
This article will consider ten of the most common use cases of a webhook gateway, with emphasis on the features offered by Convoy. Convoy is a high-performance open-source webhooks gateway to which platform teams can deploy to manage their webhooks end to end. &lt;/p&gt;

&lt;p&gt;The use cases to be discussed are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reliable Webhook Delivery&lt;/li&gt;
&lt;li&gt;Routing in Microservices&lt;/li&gt;
&lt;li&gt;Routing with Message Brokers&lt;/li&gt;
&lt;li&gt;Static IPs&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Scale&lt;/li&gt;
&lt;li&gt;Monitoring and Alerts&lt;/li&gt;
&lt;li&gt;Easy Debugging&lt;/li&gt;
&lt;li&gt;Geolocation Based Routing&lt;/li&gt;
&lt;li&gt;Versioning&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reliable Webhook Delivery
&lt;/h2&gt;

&lt;p&gt;Webhooks–which are simply HTTP push– fail frequently due to reasons that include but are not limited to downtimes, spikes in network requests, flaky internet, and expired SSL certificates.&lt;/p&gt;

&lt;p&gt;A webhook gateway solves this problem by implementing features such as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retries: to automatically resend webhook events a specified number of times.&lt;/li&gt;
&lt;li&gt;Circuit breaking: to stop sending requests to endpoints that have failed consecutively, thereby not wasting resources on a dead endpoint.&lt;/li&gt;
&lt;li&gt;Rate limiting: to prevent overloading client endpoints by delivering events at a rate that the client can process.&lt;/li&gt;
&lt;li&gt;Timeouts; to prevent waiting endlessly for response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A webhook gateway like Convoy exists to ensure the deliverability of your webhook events by implementing these features, while the rest of your application handles other business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing in Microservices
&lt;/h2&gt;

&lt;p&gt;In a microservice architecture, many of the services send out webhook events, they may also receive events from third-party providers. How do you manage these ingress and egress webhooks traffic? A webhook gateway can be deployed and configured to route incoming webhooks to the services where they are to be consumed, in the same manner, it would receive webhook events from your services and forward them to client endpoints. This flow of events is illustrated in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nA5a8QQg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lqdmv8x52asca04wx4df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nA5a8QQg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lqdmv8x52asca04wx4df.png" alt="Diagram illustrating flow of events from microservices to client endpoint and vice versa" width="800" height="520"&gt;&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://getconvoy.io/blog/why-do-microservices-need-a-webhooks-gateway"&gt;Why do microservices need a Webhooks Gateway?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Webhook gateways are able to determine the destination of webhook events by checking the header and payload of each request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing with Message Brokers
&lt;/h2&gt;

&lt;p&gt;Webhook events are asynchronous by nature and Message Brokers are designed to facilitate asynchronous communication, hence, your webhook deployment that integrates with message brokers allow you to completely bypass the shortcomings of REST API&lt;br&gt;
Connections when sending webhook events.&lt;/p&gt;

&lt;p&gt;Convoy recently announced the release of its Message Broker integrations to ingest webhooks from backend services to Convoy. For API providers, this means that they will be able to ingest webhook events from their backend services into Convoy using Message brokers. Convoy currently supports Google PubSub and Amazon SQS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wC-n5oed--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qdax13hehksubkl5vxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wC-n5oed--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qdax13hehksubkl5vxp.png" alt="message broker ingestion diagram" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram above illustrates Convoy pulling webhook events from a message queue and then sending them to a client endpoint. Convoy allows you to send events through both message brokers and a REST API. Read more about it in this &lt;a href="https://getconvoy.io/blog/webhooks-with-message-brokers-and-convoy/"&gt;blog post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static IPs
&lt;/h2&gt;

&lt;p&gt;If you are an API provider that sends webhook to consumers who are in sectors where security is a top priority, they may have strict security policies that include whitelisting IPs for all inbound webhooks on their firewalls. To serve these consumers, you need to provide them with a Static IP address from which your events would be coming for them to whitelist. However, most modern ways of deploying applications make it such that IP addresses are ephemeral by default instead of Static.&lt;/p&gt;

&lt;p&gt;One way to fix this is to deploy a webhook gateway that would serve as a forward proxy for your webhook-sending application. Convoy allows you to configure your outbound webhook events with a forward proxy which will have a Static IP. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3H9NVcpF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5q8biazq39oxx9bxxknx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3H9NVcpF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5q8biazq39oxx9bxxknx.jpg" alt="diagram that show how the IP of an outbound request changes as it is transmitted to a client's endpoints" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simplified diagram shows how the IP of your outbound requests changes as it is transmitted to a client's endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;On the consumer end, the webhook gateway is still the ideal point to implement several security mechanisms. Both the webhook publisher and the webhook consumer play complementary roles to secure requests against malicious attackers. Some security measures that API providers and consumers can apply at the webhook gateway level include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signed Payload: An API publisher can prevent a replay attack when they sign their payload and include a generated timestamp to their signature on each request. On the consumer side, the signed payload would be matched against each expected variable, to verify that the signature has not been altered.&lt;/li&gt;
&lt;li&gt;Static IP: A consumer app would be able to verify that the Static IP address of all incoming webhook events is whitelisted.&lt;/li&gt;
&lt;li&gt;Mutual TLS: Consumer apps equally use mutual TLS to ensure that connection requests from third-party providers are truly from the said provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scale
&lt;/h2&gt;

&lt;p&gt;We live in a world of APIs. Since APIs are the glue that holds the digital world together, it is not surprising that billions of webhooks are delivered every day. Below is an example of a &lt;a href="https://twitter.com/ShopifyEng/status/1597983929654710278?s=20&amp;amp;t=imFyGdlmjo16ZNAm6ZFfnw"&gt;Shopify BFCM Webhooks stats&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JgxaAyrC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5rfk73squuuk7empkht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JgxaAyrC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5rfk73squuuk7empkht.png" alt="Shopify BFCM Webhooks stats" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a webhook gateway handling all of your webhook traffic, you can independently scale up and down during and after peak periods. This also means that your webhooks implementations stay decoupled from the rest of your backend services. If your webhook gateway is a SaaS like &lt;a href="https://dashboard.getconvoy.io/"&gt;Convoy&lt;/a&gt;, the developers in your product team do not have to get deeply involved in the webhook implementation nor get caught up in its complexity, permitting them to focus on your core business product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Alerts
&lt;/h2&gt;

&lt;p&gt;Webhooks fail all the time, one reason is that App owners are constantly pushing changes to their apps. It is good behaviour for a webhook delivery system to be able to inform the app owner when their app is down. Convoy does well in this area as it allows you to implement different solutions for monitoring. For example, you can monitor uptime or monitor the average request per minute on your webhooks route, and flag it when they're below a certain threshold. Also, after an endpoint consistently fails, Convoy disables the endpoint and sends an email to the developers to triage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy Debugging
&lt;/h2&gt;

&lt;p&gt;In addition to enabling monitoring, a webhook gateway can make it easier to debug failed webhook requests. For example, Convoy includes a web interface where an operator can filter through event logs, search through the webhook payloads, see the response body from each request, discover the point where a webhook is failing, and even manually resend these webhooks. The dashboard is available to you whether you are an API provider or a consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Geolocation-Based Routing
&lt;/h2&gt;

&lt;p&gt;Another possible way to use your webhook gateway to scale your product is to deploy instances of it at various locations closer to your consumers. Routing webhook requests based on clients' location works similarly to the traditional API Geolocation Based Routing. There are several factors to put into consideration when deciding whether you need a geo-location-based routing solution. But the end result is the same when you do decide to implement it: you primarily mitigate issues caused by latency. &lt;/p&gt;

&lt;p&gt;To illustrate how this would work in a real application; your backend services are deployed in a server in the UK, but you have consumer apps running in India. When you deploy Convoy as your webhook gateway closer to India and write webhook events to a message broker, Convoy automatically receives them and then routes them to your clients in India. Routing these events to clients in India would involve lesser network overhead because of the location of your webhook gateway.&lt;/p&gt;

&lt;p&gt;This practice is common with API gateways, we think that in the case of a webhook gateway, this might only be relevant for a large company with several consumers in distant parts of the world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning
&lt;/h2&gt;

&lt;p&gt;Webhook requests signatures evolve over time either in response to bug reports or the need for a new feature, but as they evolve, versioning these signatures become important as you gradually face out old features. Versioning also allows API consumers to adopt new changes to the signature at their own pace. The Convoy OSS and Cloud software also supports this&lt;br&gt;
feature.&lt;/p&gt;

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

&lt;p&gt;So far you have seen the most common ways that one can utilize a webhook gateway.&lt;br&gt;
Convoy democratizes the best features of webhook infrastructures used at top tech companies such as Stripe and PagerDuty into a single binary. When setting up Convoy for your product, you can pick any of these features that suit your needs right out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help me out!
&lt;/h2&gt;

&lt;p&gt;If you feel like this article helped you understand Webhooks better! I would be super happy if you could give us a star! And let me also know in the comments ❤️&lt;br&gt;
&lt;a href="https://github.com/frain-dev/convoy"&gt;https://github.com/frain-dev/convoy&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8c4b2_kL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjooh4nmxs1wvo6rk348.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8c4b2_kL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjooh4nmxs1wvo6rk348.gif" alt="please gif" width="408" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What are Reverse-API Gateways?</title>
      <dc:creator>Lotanna Nwose</dc:creator>
      <pubDate>Fri, 09 Jun 2023 08:51:05 +0000</pubDate>
      <link>https://dev.to/convoy/what-are-reverse-api-gateways-13gd</link>
      <guid>https://dev.to/convoy/what-are-reverse-api-gateways-13gd</guid>
      <description>&lt;h2&gt;
  
  
  In this article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Introduction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What are Webhook Gateways&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Why use a Webhook Gateway&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Architecture of Webhook Gateways&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API Gateways vs Webhook Gateways&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conclusion&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Webhooks are known as reverse APIs; this is because they reverse the usual flow of communication. So, instead of the client requesting data from the server(APIs), the server sends data to the client when a specific event occurs(Webhooks).&lt;br&gt;
Now that you know this, let us dive into today's agenda!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fikn56qtol6lkgdh6yc2c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fikn56qtol6lkgdh6yc2c.gif" alt="Let's get started Gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Webhook Gateways?
&lt;/h2&gt;

&lt;p&gt;A webhook gateway is a webhook management tool that sits between a webhook provider and webhook consumer. It acts as a reverse and forward proxy for webhooks. It can be deployed by both the API provider that sends webhooks and the consumer that receives webhooks to handle webhook events multiplexing and de-multiplexing respectively. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a Webhook Gateway?
&lt;/h2&gt;

&lt;p&gt;Speed is a competitive advantage in today's ever-changing user requirements. Development teams are required to move fast and deliver user value. More teams are adopting a serverless, micro-service, and service-oriented architecture to move fast. Teams are leveraging technologies like an API Gateway to consolidate duplicate tasks like authentication, rate limiting, circuit breaking etc. while product teams focus on their core business. With webhooks, teams and tech leads would need to answer the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How can we provide a consistent infrastructure to receive events from third-party providers and route these events to one or more micro-services to handle them?&lt;/li&gt;
&lt;li&gt;How can we provide a consistent infrastructure to collect events from multiple backend services and send them to client endpoints?&lt;/li&gt;
&lt;li&gt;How can we consolidate duplicate webhook responsibilities like endpoint authentication and security, rate limiting, and endpoint failure notifications?&lt;/li&gt;
&lt;li&gt;Similar to API Gateways, how can we increase developer efficiency across the organisation, where webhooks become plug &amp;amp; play?&lt;/li&gt;
&lt;li&gt;How can we provide an independent infrastructure to scale to handle webhooks traffic in peak periods? See &lt;a href="https://twitter.com/ShopifyEng/status/1597983929654710278?s=20&amp;amp;t=imFyGdlmjo16ZNAm6ZFfnw" rel="noopener noreferrer"&gt;Shopify BFCM Webhooks Stats&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your challenge is offering developers in your organisation a simple and dependable experience in the face of any webhook complexity. A webhook gateway is a way to decouple all your microservices from your webhook implementations. When a microservice needs to send a webhook event, it should write to the broker, and the webhook gateway receives it and routes them to the right endpoint(s), whilst keeping track of everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture of Webhook Gateways
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftxvgjsodle2r0kcixlny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftxvgjsodle2r0kcixlny.png" alt="Webhooks Gateway Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram above provides a high-level view of how a webhook gateway operates. We used AWS-specific elements but this can be any cloud environment - Azure, GCP, Digital Ocean, etc or even On-Prem. The arrows from the left show the flow of traffic from your backend services to client endpoints on the right, and the arrows from the right show the flow of traffic from third-party providers like (stripe, Twilio, etc.) to your backend services for processing. &lt;/p&gt;

&lt;p&gt;Generally, to avoid vendor lock-in, a webhook gateway will (should) support multiple broker systems like Amazon SQS, Google PubSub, Kafka etc. to allow you to use the best broker for your scenario. Visit this &lt;a href="https://getconvoy.io/docs/deploy/architecture" rel="noopener noreferrer"&gt;page&lt;/a&gt; to learn about Convoy's internal architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Gateways vs. Webhook Gateways
&lt;/h2&gt;

&lt;p&gt;Webhook gateways share similarities with API gateways such as Tyk and Kong&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;API Gateway&lt;/th&gt;
&lt;th&gt;Webhooks Gateway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrypoint&lt;/td&gt;
&lt;td&gt;Entry into the API.&lt;/td&gt;
&lt;td&gt;Exit from the API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key Metric&lt;/td&gt;
&lt;td&gt;Throughput &amp;amp; Latency&lt;/td&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Type&lt;/td&gt;
&lt;td&gt;Synchronous API&lt;/td&gt;
&lt;td&gt;Asynchronous API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocols&lt;/td&gt;
&lt;td&gt;Multiple Protocols - HTTP, Websockets, gRPC&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message Format&lt;/td&gt;
&lt;td&gt;JSON, XML &amp;amp; Protocol Buffers.&lt;/td&gt;
&lt;td&gt;Mostly JSON.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;td&gt;Stateful&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;Convoy is the first ever open-source webhooks gateway to manage millions of webhooks end-to-end. If you’re exploring solutions for your webhooks, we welcome you to try out &lt;a href="https://bit.ly/convoywebhooks" rel="noopener noreferrer"&gt;Convoy webhooks Gateway&lt;/a&gt; for free today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help me out!
&lt;/h2&gt;

&lt;p&gt;If you feel like this article helped you understand Webhooks better! I would be super happy if you could give us a star! And let me also know in the comments ❤️&lt;br&gt;
&lt;a href="https://bit.ly/convoywebhooks" rel="noopener noreferrer"&gt;https://bit.ly/convoywebhooks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdc0nm6k9ysn9rfm636yd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdc0nm6k9ysn9rfm636yd.gif" alt="Cat meme Gif"&gt;&lt;/a&gt;&lt;br&gt;
Thanks for reading!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>node</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Publish Webhooks From Your FastAPI API With Convoy</title>
      <dc:creator>Abdulazeez Abdulazeez</dc:creator>
      <pubDate>Mon, 21 Nov 2022 16:07:18 +0000</pubDate>
      <link>https://dev.to/convoy/publish-webhooks-from-your-fastapi-api-with-convoy-1n9d</link>
      <guid>https://dev.to/convoy/publish-webhooks-from-your-fastapi-api-with-convoy-1n9d</guid>
      <description>&lt;p&gt;Webhooks are messages ( or payload ) sent from an application on the execution of an operation. They are also used to communicate between a chain of services; for example, a payment provider emits webhook events to an e-commerce application’s endpoint after an operation.&lt;/p&gt;

&lt;p&gt;Convoy facilitates publishing webhook events from your application to your clients by serving as a reliable egress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Unlike traditional webhook servers, Convoy allows you to retry and replay your webhook events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article, you will learn how to use Convoy’s Python SDK to publish webhooks events to multiple endpoints in your FastAPI application. You’ll start by setting up your Convoy instance on &lt;a href="https://dashboard.getconvoy.io" rel="noopener noreferrer"&gt;Convoy cloud&lt;/a&gt; ( or &lt;a href="https://getconvoy.io/docs/deploy/overview#configure" rel="noopener noreferrer"&gt;Convoy OSS&lt;/a&gt; ), create a todo API, and lastly, integrate the Convoy SDK to publish webhook events to endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Convoy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To set up your Convoy instance, sign in to your dashboard and create a new project:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fconvoy-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fconvoy-dashboard.png" alt="Convoy dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new outgoing project from your dashboard. An outgoing project is a project for sending out webhook events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-fastapi-project.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-fastapi-project.png" alt="Create an outgoing project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A project API key will be generated for the newly created outgoing project. Store the key in a safe place as you’ll use it in the API to authenticate your Convoy instance to publish webhooks:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fapi-key.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fapi-key.png" alt="Project API Key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure your outgoing project. For this article, you’ll be configuring your outgoing project manually. Select the ***&lt;strong&gt;&lt;em&gt;Setup Without SDK&lt;/em&gt;&lt;/strong&gt;*** option to manually configure your outgoing project:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fconfigure-project.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fconfigure-project.png" alt="Configure project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡All actions performed manually in this article can be performed via our &lt;a href="https://getconvoy.io/docs/sdk" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create an application&lt;/strong&gt;: application refers to your backend app with valid endpoints where webhooks are delivered to. An application can contain as many endpoints as necessary:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-application.png" alt="Create an application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Applications are created per user to distinguish their webhook events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create an endpoint&lt;/strong&gt;: an endpoint is a specific destination that can receive webhook events. The endpoint URL used in this article is generated from &lt;a href="https://webhooks.site" rel="noopener noreferrer"&gt;webhooks.site&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-endpoint.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-endpoint.png" alt="Create an endpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a subscription for the application&lt;/strong&gt;: Subscriptions on Convoy connect events to their respective application endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-subscription.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fcreate-subscription.png" alt="Create application subscription"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re all set to publish events and monitor them from your dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fevents-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fevents-dashboard.png" alt="Events dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With your Convoy instance up and running, you’ll build the API to publish your webhook events in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;The first step to publishing webhooks from your FastAPI-powered API is to build the API. Start by creating a new folder and creating a virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir fastapi-todo-convoy &amp;amp;&amp;amp; cd fastapi-todo-convoy
$ python3 -m venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ You may use a different virtual environment manager like &lt;a href="https://pipenv.pypa.io/" rel="noopener noreferrer"&gt;Pipenv&lt;/a&gt; or &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;poetry&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  API structure
&lt;/h3&gt;

&lt;p&gt;The API folder structure is outlined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi-todo-convoy
 +- .env # store Convoy API key and application ID.
 +- api/
   +- __init__.py # module file
   |
   +- config.py # interact with the environment file
   | 
   +- api.py # todo API source file
   | 
   +- events.py # store for various event types for your webhook payload
 +- main.py # entrypoint to run the application
 +- requirements.txt # application requirements file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the project folder, add the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch .env api/{config,api,events}.py main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install dependencies
&lt;/h3&gt;

&lt;p&gt;As a next step, activate the virtual environment and install the following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ source venv/bin/activate
$ pip install fastapi uvicorn pydantic[dotenv]
$ pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, install the &lt;code&gt;convoy-python&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pip install git+ssh://git@github.com/frain-dev/convoy-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set environment variables
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;.env&lt;/code&gt; file, add the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONVOY_API_KEY=&amp;lt;your-api-key&amp;gt;
CONVOY_APP_ID=&amp;lt;your-app-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;your-api-key&amp;gt;&lt;/code&gt; in the environment file above with the API key generated earlier on and retrieve your Convoy application ID from the dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fapplication-id.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fapplication-id.png" alt="Retrieve application ID"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To allow your API to retrieve the values stored in the environment file, define a &lt;code&gt;BaseSettings&lt;/code&gt; child class in &lt;code&gt;config.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from pydantic import BaseSettings
from typing import Optional

class Settings(BaseSettings):
    # convoy credentials
    CONVOY_API_KEY: Optional[str] = None
    CONVOY_APP_ID: Optional[str] = None

    class Config:
        env_file = ".env"

settings = Settings()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Settings&lt;/code&gt; class defined above reads the variables defined in the environment file. The API can directly access the variables from the instance variable of the &lt;code&gt;Settings&lt;/code&gt; class &lt;code&gt;settings&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define event types
&lt;/h2&gt;

&lt;p&gt;The next step is to define the various event payload to be included in the webhook to be published from the APIs. In &lt;code&gt;[events.py](http://events.py)&lt;/code&gt;, add the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events = {
    "ping": {
        "event": "ping",
        "description": "Webhook test from application."
    },
    "created": {
        "event": "todo.created",
        "description": "Todo created successfully"
    },
    "retrieved": {
        "event": "todo.retrieved",
        "description": "Todo retrieved successfully"
    },
    "updated": {
        "event": "todo.updated",
        "description": "Todo updated successfully"
    },
    "deleted": {
        "event": "todo.deleted",
        "description": "Todo deleted successfully"
    },
    "failed": {
        "event": "todo.failure",
        "description": "Todo not found."
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code block above, you have six event types all prefixed with &lt;code&gt;todo.&lt;/code&gt; accompanied by a description of the event.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the API
&lt;/h2&gt;

&lt;p&gt;The API performs a Create, Read, Update and Delete ( CRUD ) operation on todos stored in an in-app database, an array variable &lt;code&gt;todos&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;api.py&lt;/code&gt;, import the dependencies and create an instance of &lt;code&gt;FastAPI&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from convoy import Convoy
from fastapi import FastAPI

from .events import events
from .config import settings

app = FastAPI()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create the &lt;code&gt;todos&lt;/code&gt; variable and an instance of Convoy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;todos = []

convoy = Convoy({"api_key": settings.CONVOY_API_KEY})
app_id = settings.CONVOY_APP_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code block above, you connect to your Convoy instance using the API key stored in the environment file using the convoy-python SDK installed. The &lt;code&gt;app_id&lt;/code&gt; variable is also set to the value stored in the environment file.&lt;/p&gt;

&lt;p&gt;Next, you’ll define a function &lt;code&gt;send_webhook_event&lt;/code&gt; that takes an argument &lt;code&gt;event_type&lt;/code&gt; and uses the &lt;code&gt;convoy.event.create()&lt;/code&gt; method to publish events to your application and by extension, your application endpoints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def send_webhook_event(event_type: str):
    event = {
        "app_id": app_id,
        "event_type": event_type,
        "data": events[event_type]
    }

    (res, err) = convoy.event.create({}, event)
    return res
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;convoy.event.create()&lt;/code&gt; method takes an optional &lt;code&gt;query&lt;/code&gt; and an &lt;code&gt;event&lt;/code&gt; payload body. The event payload body is an object containing the application ID, the event type and payload. In your API, the event type and event payload have been defined in &lt;code&gt;events.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the webhook publisher function in place, add the code below to complete your API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.get("/")
async def ping():
    send_webhook_event("ping")
    return {"message": "Wilkomen!"}

@app.get("/todo", tags=["todos"])
async def get_todos() -&amp;gt; dict:
    send_webhook_event("retrieved")
    return { "data": todos }

@app.post("/todo", tags=["todos"])
async def add_todo(todo: dict) -&amp;gt; dict:
    todos.append(todo)
    send_webhook_event("created")
    return {
        "data": { "Todo added." }
    }

@app.put("/todo/{id}", tags=["todos"])
async def update_todo(id: int, body: dict) -&amp;gt; dict:
    for todo in todos:
        if int(todo["id"]) == id:
            todo["item"] = body["item"]
            send_webhook_event("updated")
            return {
                "data": f"Todo with id {id} has been updated."
            }
    send_webhook_event("failed")
    return {
        "data": f"Todo with id {id} not found."
    }

@app.delete("/todo/{id}", tags=["todos"])
async def delete_todo(id: int) -&amp;gt; dict:
    for todo in todos:
        if int(todo["id"]) == id:
            todos.remove(todo)
            send_webhook_event("deleted")
            return {
                "data": f"Todo with id {id} has been removed."
            }

    send_webhook_event("failed")
    return {
        "data": f"Todo with id {id} not found."
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have defined the routes to perform various CRUD operations in the code block above. Each successful operation emits a webhook to your Convoy application, and a failed operation emits a failed webhook event.&lt;/p&gt;

&lt;p&gt;Lastly, in the API’s entry point file &lt;code&gt;main.py&lt;/code&gt;, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import uvicorn

if __name__ == "__main__":
    uvicorn.run('api.api:app', host="0.0.0.0", port=8080, reload=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above starts the API on port 8080 on the localhost. Start your application with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Publishing webhooks
&lt;/h2&gt;

&lt;p&gt;It’s time to publish your first webhook! To publish your first webhook event, send a cURL request to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://0.0.0.0:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cURL request returns a response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`{"message":"Wilkomen!"}`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your Convoy dashboard, the event is delivered and logged:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fping-webhook-event.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fping-webhook-event.png" alt="Ping event"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Event Deliveries&lt;/strong&gt; tab, you can verify the status of the delivery as well as view the number of attempts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fevent-deliveries-i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fevent-deliveries-i.png" alt="event-deliveries-i"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on the event, a detailed delivery page is displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fping-event-details.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fping-event-details.png" alt="Event detail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s create a todo using a cURL request and verify if your API will publish a webhook event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -X 'POST' \
  'http://0.0.0.0:8080/todo' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": 1,
  "item": "Create a Convoy outgoing project today"
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API returns a successful response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": [
    "Todo added."
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Was the webhook for a &lt;strong&gt;create&lt;/strong&gt; action emitted? Verify from your dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fevents-dashboard-ii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fevents-dashboard-ii.png" alt="Events dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can verify that the API webhook successfully emitted the event type &lt;code&gt;created&lt;/code&gt; from the dashboard. A detailed view of the event delivery can is seen from the event delivery tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Ftodo-created-webhook.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Ftodo-created-webhook.png" alt="Todo created webhook event"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A visit to the endpoint URL strengthens the webhook delivery status from the Convoy dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fwebhook-endpoint.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgetconvoy.io%2Fblog-assets%2Fwebhook-endpoint.png" alt="Webhook delivery endpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go on, execute more operations on the todo API and publish webhooks today on Convoy!&lt;/p&gt;

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

&lt;p&gt;Convoy provides the ability to publish webhooks to various endpoints in one request. In this article, you learned how to create and configure a Convoy outgoing project and publish webhooks from an API you built to your configured endpoint.&lt;/p&gt;

&lt;p&gt;Convoy provides you with reliability and replayability out of the box. If this sounds suitable for your architecture,&lt;a href="https://dashboard.getconvoy.io/" rel="noopener noreferrer"&gt;try it out&lt;/a&gt;and give us feedback on our&lt;a href="https://convoy-community.slack.com/join/shared_invite/zt-xiuuoj0m-yPp~ylfYMCV9s038QL0IUQ#/shared-invite/email" rel="noopener noreferrer"&gt;slack&lt;/a&gt;community!&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>beginners</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Let's implement Stripe webhook signatures in Golang</title>
      <dc:creator>Abdulazeez Abdulazeez</dc:creator>
      <pubDate>Fri, 04 Nov 2022 10:26:00 +0000</pubDate>
      <link>https://dev.to/convoy/lets-implement-stripe-webhook-signatures-in-golang-eoo</link>
      <guid>https://dev.to/convoy/lets-implement-stripe-webhook-signatures-in-golang-eoo</guid>
      <description>&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This article originally appeared on the &lt;a href="https://getconvoy.io/blog"&gt;GetConvoy Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building a webhook publishing infrastructure requires providing a way to validate the message’s integrity to enable consumers to validate the webhook event origin. Generating webhook signatures would require us to implement certain important features which include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prevent Replay Attacks&lt;/li&gt;
&lt;li&gt;Forward Compatibility.&lt;/li&gt;
&lt;li&gt;Zero Downtime Key Rotation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More on each item later on. But these properties exist in Stripe’s webhook signature implementation, see below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Stripe-Signature:
&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1492774577,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this article, we would replicate this implementation in Golang, with one additional requirement; We want our implementation to be backward compatible with the common implementation, like the below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Stripe-Signature: 
5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This backward compatibility allows new API consumers to opt-in to this new system at their choosing. For the purpose of this article, we define the latter signature specification as simple signatures and the former as advanced signatures. Implementing simple signatures is pretty trivial and commonplace, but advanced signatures aren’t common.&lt;/p&gt;

&lt;p&gt;We start by breaking down the requirements to discuss advanced signatures even further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prevent Replay Attacks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Stripe-Signature:
&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1492774577,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A replay attack occurs when an attacker intercepts a valid payload, and its signature, then re-transmits them. The aim is to exploit unsuspecting webhook consumers to perform an action multiple times. Idempotent keys aren’t sufficient against attacks like this because, webhooks are transient data, assuming consumers purge their webhook log after a certain period, it means re-transmitting purged webhook events all of sudden becomes valid. &lt;/p&gt;

&lt;p&gt;To mitigate against this, we generate a timestamp and include it in the signed payload, so it is verified alongside the signature, so the attacker cannot change the timestamp without invalidating the signature. This ensures that events after a given threshold are regarded invalid. &lt;/p&gt;

&lt;p&gt;When retrying events each delivery attempt should re-generate the timestamp, this ensures the timestamp is fresh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forward Compatibility
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Stripe-Signature:
&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1492774577,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Webhooks implementation evolves over time, providers can determine whether to switch from &lt;code&gt;hex&lt;/code&gt; to &lt;code&gt;base64&lt;/code&gt; for encoding or change the hash function or the template of the payload being signed. To enable smooth upgrades for consumers, we version signatures in the example above. This allows consumers to verify against at least just one signature and migrate to the newest version at their convenience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero Downtime Key Rotation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Stripe-Signature:
&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1492774577,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xdz+2j9aMVQUUjSy0KUz/CsjD4jaD6wHJGGf1c3eZzrWxHTf1cAjZ3aL07O9NZXMhg5gajfi+TYuBU1aoU18xA&lt;span class="o"&gt;==&lt;/span&gt;,
&lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
&lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cvt+CsjD4jaD6wHJGGf1/2j9aMVQUUjSy0KUzc3eZzrWxHTf1cTYuBU1aoU18xAAjZ3aL07O9+NZXMhg5gajfi&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;df51a848684dac3901d2b8bd17e5c8d2d971b15c544fa923493232df1fe0fbad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Webhooks rely on a shared secret that needs to be rotated periodically to be kept safe. Building effective key rotation mechanisms is a major implementation of great webhooks implementations. In the above sample, you can see &lt;code&gt;v1&lt;/code&gt; and &lt;code&gt;v0&lt;/code&gt; appear twice this means, we used two secrets to generate two different schemes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Implementation
&lt;/h3&gt;

&lt;p&gt;The core implementation goes thus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Scheme&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Secret represents a list of active secrets used for&lt;/span&gt;
    &lt;span class="c"&gt;// a scheme. It is used to implement rolled secrets.&lt;/span&gt;
    &lt;span class="c"&gt;// Its order is irrelevant.&lt;/span&gt;
    &lt;span class="n"&gt;Secret&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

    &lt;span class="n"&gt;Hash&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Encoding&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Signature&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawMessage&lt;/span&gt;

    &lt;span class="c"&gt;// The order of these Schemes is a core part of this API.&lt;/span&gt;
    &lt;span class="c"&gt;// We use the index as the version number. That is:&lt;/span&gt;
    &lt;span class="c"&gt;// Index 1 = v1, Index 2 = v2&lt;/span&gt;
    &lt;span class="n"&gt;Schemes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;

    &lt;span class="c"&gt;// This flag allows for backward-compatible implementation&lt;/span&gt;
    &lt;span class="c"&gt;// of this type. You're either generating a simple header&lt;/span&gt;
    &lt;span class="c"&gt;// or a complex header.&lt;/span&gt;
    &lt;span class="n"&gt;Advanced&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;

    &lt;span class="c"&gt;// This function is used to generate a timestamp for signing&lt;/span&gt;
    &lt;span class="c"&gt;// your payload. It was only added to aid testing.&lt;/span&gt;
    &lt;span class="n"&gt;generateTimestampFn&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ComputeHeaderValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Encode Payload&lt;/span&gt;
    &lt;span class="n"&gt;tBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encodePayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Generate Simple Signatures&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Advanced&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schemes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schemes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;sec&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tBuf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Generate Advanced Signatures&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;signedPayload&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Builder&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hStr&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Builder&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

    &lt;span class="c"&gt;// Add timestamp.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generateTimestampFn&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generateTimestampFn&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="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Generate Payload&lt;/span&gt;
    &lt;span class="n"&gt;signedPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signedPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signedPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tBuf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// Generate Header&lt;/span&gt;
    &lt;span class="n"&gt;tPrefix&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"t=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hStr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tPrefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schemes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;",v%d="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hSig&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;sch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secret&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signedPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;hSig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;hStr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hSig&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="n"&gt;hStr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down the above code listing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We use the &lt;code&gt;Advanced&lt;/code&gt; flag to determine what type of signature to generate.&lt;/li&gt;
&lt;li&gt;We use the &lt;code&gt;Scheme&lt;/code&gt; type to encapsulate all versions, and the order in which they’re passed in determines their version. We map to index 0 to &lt;code&gt;v1&lt;/code&gt; etc similar to how we don’t define API versioning as &lt;code&gt;/api/v0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ComputeHeaderValue&lt;/code&gt; will either generate a simple signature string or an advanced signature string based on the &lt;code&gt;Advanced&lt;/code&gt; flag.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Other aspects of this library were removed for brevity, you can find the full code &lt;a href="https://github.com/frain-dev/convoy/blob/main/pkg/signature/signature.go"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SDK
&lt;/h3&gt;

&lt;p&gt;To enable easy migration, we’ve added webhook verification logic to our &lt;a href="https://github.com/frain-dev/convoy.rb"&gt;Ruby&lt;/a&gt;, &lt;a href="https://github.com/frain-dev/convoy-python"&gt;Python&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/frain-dev/convoy-go"&gt;Golang&lt;/a&gt; SDKs to parse and validate this format. This verification will automatically identify either simple or advanced signatures and validate them respectively. &lt;/p&gt;

&lt;h3&gt;
  
  
  Separate API Keys from Webhook Secrets
&lt;/h3&gt;

&lt;p&gt;Another useful benefit of Advanced Signatures is we can stop using our API Keys as webhooks secrets with zero downtime. This is good because compromised webhook secrets do not equal compromised API Keys and vice-versa. This is also known as the principle of least privilege. To achieve this do the following: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create endpoints, and set the webhook secret as the API Key.&lt;/li&gt;
&lt;li&gt;Update your apps to verify Advanced Signatures.&lt;/li&gt;
&lt;li&gt;Roll over the current webhook secret with an expiry time. &lt;/li&gt;
&lt;li&gt;Set the new webhook secret in your apps. 🎉&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this article, we showed how to implement Stripe-Like webhooks and build them in a backward-compatible fashion with your current implementation. We shipped this feature to Convoy OSS and Cloud. You can sign up &lt;a href="https://dashboard.getconvoy.io"&gt;here&lt;/a&gt; to get started!&lt;/p&gt;

</description>
      <category>go</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Publish An Event to Multiple Endpoints using Convoy</title>
      <dc:creator>Daniel Oluojomu</dc:creator>
      <pubDate>Fri, 11 Mar 2022 13:05:50 +0000</pubDate>
      <link>https://dev.to/convoy/publish-an-event-to-multiple-endpoints-using-convoy-27b3</link>
      <guid>https://dev.to/convoy/publish-an-event-to-multiple-endpoints-using-convoy-27b3</guid>
      <description>&lt;p&gt;One common scenario in publishing webhook events is enabling a user to provide multiple endpoints to receive events. One easy example of this is publishing an event that the user needs to process at more than one location. This location could be a no-code platform like zapier, a newly minted microservice or serverless function, or a good old slack notification. In this article, I’d like to explain how you can achieve this using &lt;a href="https://github.com/frain-dev/convoy" rel="noopener noreferrer"&gt;Convoy&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%2Fx8uhw574k6txpm6plr1b.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%2Fx8uhw574k6txpm6plr1b.png" alt="Infrastructure Representation" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without Convoy, your users have to build in the fan-out mechanism themselves which is a lot more stressful. &lt;/p&gt;

&lt;h3&gt;
  
  
  Steps
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Start Convoy Instance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To follow through with this article you’d need to run an instance of Convoy:&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="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 5005:5005 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; convoy-server &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/convoy.json:/convoy.json &lt;span class="se"&gt;\&lt;/span&gt;
    ghcr.io/frain-dev/convoy:v0.4.18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create an Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we have to create an application under this group.&lt;/p&gt;

&lt;p&gt;Sample Payload&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"support_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiJ9"&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;Bash&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--data&lt;/span&gt; @app.json &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;
    http://localhost:5005/api/v1/applications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"App created successfully"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2b1e9973-ed03-403c-a8b0-341edd51fb14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"group_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f0a187f4-edaa-4f8e-adec-75b9a36b3c68"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"support_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"endpoints"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T14:17:51.111+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T14:17:51.111+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"events"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create Two Endpoints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we can create multiple endpoints under this app. The first endpoint will take the &lt;code&gt;*&lt;/code&gt; event type. Essentially this event type means all incoming events to that app will be published to that endpoint. The second endpoint will take the event type &lt;code&gt;payment.created&lt;/code&gt;. Only incoming with the exact type &lt;code&gt;payment.created&lt;/code&gt; will be published to that endpoint. Convoy tries to match the event type to all available endpoints under that app, the event is then published to all the matched endpoints.&lt;/p&gt;

&lt;p&gt;For the first endpoint:&lt;/p&gt;

&lt;p&gt;Sample Payload&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-endpoint-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"events"&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="s2"&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;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-endpoint&amp;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;Bash&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--data&lt;/span&gt; @endpoint-1.json &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;
    http://localhost:5005/api/v1/applications/&lt;span class="o"&gt;{&lt;/span&gt;appID&lt;span class="o"&gt;}&lt;/span&gt;/endpoints
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"App endpoint created successfully"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2901bbc9-092e-4685-868d-a17298fe86ba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-endpoint&amp;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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-endpoint-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"events"&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="s2"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T14:18:14.493+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T14:18:14.493+01:00"&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;For the second endpoint:&lt;/p&gt;

&lt;p&gt;Sample Payload&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-endpoint-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"events"&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="s2"&gt;"payment.created"&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;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-endpoint&amp;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;Bash&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--data&lt;/span&gt; @endpoint-2.json &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;
    http://localhost:5005/api/v1/applications/&lt;span class="o"&gt;{&lt;/span&gt;appID&lt;span class="o"&gt;}&lt;/span&gt;/endpoints
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"App endpoint created successfully"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2901bbc9-092e-4685-868d-a17298fe86ba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-endpoint&amp;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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-endpoint-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"events"&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="s2"&gt;"payment.created"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T14:18:14.493+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T14:18:14.493+01:00"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Publish Event&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now let us publish an event with the type &lt;code&gt;payment.created&lt;/code&gt; to this app. The &lt;code&gt;payment.created&lt;/code&gt; will match both endpoints, since &lt;code&gt;*&lt;/code&gt; will match all event types, and the &lt;code&gt;payment.created&lt;/code&gt; type of the second endpoint matches exactly.&lt;/p&gt;

&lt;p&gt;Sample Payload&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;"app_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2b1e9973-ed03-403c-a8b0-341edd51fb14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"blog"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"medium.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment.created"&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;Bash&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--data&lt;/span&gt; @event.json &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;
    http://localhost:5005/api/v1/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"App event created successfully"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ddcd3928-1d7e-4527-901f-47673fd569ce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment.created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_endpoints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"provider_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"blog"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium.com"&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;"app_metadata"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2b1e9973-ed03-403c-a8b0-341edd51fb14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"group_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e6bbde4b-4c43-45a6-8d4c-c8eed1c2bb41"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"support_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test@gmail.com"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T15:14:27.569+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-09T15:14:27.569+01:00"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Show Endpoint Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://convoy.ghost.io/content/images/2022/03/output-1-2.gif" rel="noopener noreferrer"&gt;Illustration of the events coming through&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Footnote
&lt;/h2&gt;

&lt;p&gt;This article was originally published on our main &lt;a href="https://getconvoy.io/blog/publish-an-event-to-multiple-endpoints-using-convoy/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. Check it out for more articles about convoy!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>cloudnative</category>
      <category>infrastructure</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Introducing Convoy</title>
      <dc:creator>Subomi Oluwalana</dc:creator>
      <pubDate>Fri, 18 Feb 2022 18:52:42 +0000</pubDate>
      <link>https://dev.to/convoy/introducing-convoy-18f4</link>
      <guid>https://dev.to/convoy/introducing-convoy-18f4</guid>
      <description>&lt;p&gt;Hi dev.to, I’d like to show you Convoy. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftppmoxz81dlbgyb0v7q7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftppmoxz81dlbgyb0v7q7.png" alt="Sample Instance UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Convoy is an open-source webhooks service. &lt;strong&gt;You can think of it as a swiss army knife for publishing webhook events.&lt;/strong&gt; Few months ago, I was building a Fintech API and I was looking around for a tool to push webhooks events. I wanted something simply, ease-to-use, language-agnostic, and cloud-native. Ideally, I wanted a container I could deploy, publish events to it, and it reliably publishes events to the endpoints. I couldn’t find a such a tool, so we built it. Convoy exposes a REST API to register endpoints, and publish events to respective endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Webhook Issues
&lt;/h3&gt;

&lt;p&gt;On the surface, webhooks isn't simply HTTP Push. There exist several naunces and some use-cases needed for some type of integrations. I'd like to talk about a few: &lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Bad Endpoints&lt;/p&gt;

&lt;p&gt;Publishing webhooks requires you to deal with several poorly designed endpoints, endpoints that respond with large payload sizes, endpoints that hang unending and eventually timeout, some other endpoints have expired certificates. A good webhook system will provide visibility into these issues for both publishers and consumers.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Delivery Attempt Logs&lt;/p&gt;

&lt;p&gt;Building against webhooks can be non-trival for developers at times. Developers often times, use ngrok to pipe events to their local machine to debug several things; signing payload, parsing payload structure, responding with a 200 status code fast, monitoring successful retries, testing url per events. Building a great webhooks delivery system requires building a delivery attempt log showing what was sent vs. what was returned.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Delayed Job Implementation.&lt;/p&gt;

&lt;p&gt;Because webhooks is inherently an asynchronous activity, It needs an underlying resilient job/task queuing system to account for scalability, retries for transient &amp;amp; non-transient failures of events. A standard webhooks delivery system is an implementation over a job queuing system (e.g. redis, kafka, rabbit, nats etc) to provide such features.  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Security&lt;/p&gt;

&lt;p&gt;Publishing webhook events with HTTP requires consumers to secure their endpoints from malicious users. Consumers need to know who is posting and if they're authorized to post events. Several consumers have several layers of security: Signing payload with a shared secret (common), Static IPs (common in fintech), Mutual TLS (found in PagerDuty). Essentially, a good webhook delivery system needs to support all forms of security depending on what respective consumers require.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;URL per Events.&lt;/p&gt;

&lt;p&gt;In this age of no-code tools, and serverless functions. It is common for developers to receive webhook events from a provider (e.g. Stripe) and fan-out to several apps behind the scene to perform any desired action. Stripe, as an example provides first-class support for this where users can subscribe an endpoint to specific events, but isn't found in many other applications. A good webhook delivery system should provide first-class support for URL per events.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is all this fragmented implementations of webhooks, we are trying to solve with &lt;a href="https://github.com/frain-dev/convoy" rel="noopener noreferrer"&gt;Convoy&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;To get started you can head over to &lt;a href="https://getconvoy.io/docs/guide" rel="noopener noreferrer"&gt;https://getconvoy.io/docs/guide&lt;/a&gt; and follow the step by step guide to running Convoy on your local machine. &lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;We believe we're still very early in abstracting the fragmented implementations of webhooks into Convoy, but we're happy to share our progress so far and invite to you be a part of our community &lt;/p&gt;

&lt;p&gt;You can join our slack community &lt;a href="https://join.slack.com/t/convoy-community/shared_invite/zt-xiuuoj0m-yPp~ylfYMCV9s038QL0IUQ" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can’t wait to hear your feedback!&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>go</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
