<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tejas Kumthekar</title>
    <description>The latest articles on Forem by Tejas Kumthekar (@tk26).</description>
    <link>https://forem.com/tk26</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F288144%2F46470f4c-da2e-49e6-aa36-f84c1e7f6b8e.jpeg</url>
      <title>Forem: Tejas Kumthekar</title>
      <link>https://forem.com/tk26</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tk26"/>
    <language>en</language>
    <item>
      <title>New RudderStack Integration: Create Actionable Insights</title>
      <dc:creator>Tejas Kumthekar</dc:creator>
      <pubDate>Wed, 08 Mar 2023 17:00:32 +0000</pubDate>
      <link>https://forem.com/courier/new-rudderstack-integration-create-actionable-insights-3ooe</link>
      <guid>https://forem.com/courier/new-rudderstack-integration-create-actionable-insights-3ooe</guid>
      <description>&lt;p&gt;Are you looking for a way to gain a clearer picture of your users and use that insight to improve your communication strategies? We've got some great news for you! Today, we're thrilled to announce an integration between Courier and &lt;a href="https://www.rudderstack.com/"&gt;Rudderstack&lt;/a&gt;, a customer data platform (CDP) that allows businesses to collect, process, and route customer event data across product, marketing, and analytics tools for better decision-making. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/21cNA4cs1XUQkN6bUZkyO8/ebd8e69ee53020b4d57d23212aae53c5/Rudderstack_Courier_data_flow.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/21cNA4cs1XUQkN6bUZkyO8/ebd8e69ee53020b4d57d23212aae53c5/Rudderstack_Courier_data_flow.png" alt="Rudderstack Courier data flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This integration builds on our &lt;a href="https://www.courier.com/blog/smarter-customer-engagement-flows-segment-integration/"&gt;recently improved integration with Segment&lt;/a&gt; and will further improve your ability to track your users' actions and take advantage of customer data stored in RudderStack to improve the notification experience for your users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Courier makes it easy to trigger notifications based on RudderStack events. We're really excited about this integration because it removes a lot of complexity for engineers and data teams looking to build a notification system."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Eric Dodds&lt;/strong&gt;, Head of Product Marketing, &lt;a href="https://www.rudderstack.com/"&gt;RudderStack&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For those who may not be familiar with RudderStack, it’s a &lt;a href="https://www.rudderstack.com/blog/what-is-a-customer-data-platform/"&gt;customer data platform&lt;/a&gt; (CDP) that allows businesses to collect, process, and route customer event data across product, marketing and analytics tools for better decision-making. As for Courier, it’s an all-in-one notifications solution that enables development teams to manage communications with their users, from product notifications to marketing messages, through a number of channels, such as email, chat, in-app messages, SMS, and push, all in one place.&lt;/p&gt;

&lt;p&gt;So, by integrating Courier with RudderStack, you get the combined benefits of both systems: you can now collect, process, and route your users' interaction data between Courier and different marketing and analytics tools, via RudderStack. This integration can help you gain a clearer picture of your users and use that insight to improve your communication strategies. &lt;/p&gt;

&lt;p&gt;You can also track notification engagement data, such as the percentage of communications that were successfully delivered, opened and read, and on what notification channels. The detailed notification analytics data gives you the potential to create more personalized app experiences for users based on their activity within your product. For example, you could adjust the way a user receives notifications based on their interactions with your product, as well as data from Salesforce, Google Ads, Mailchimp, and other RudderStack sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  More about Courier
&lt;/h2&gt;

&lt;p&gt;Courier is an API and web studio that centralizes all product-triggered communications, such as email, chat, in-app messages, SMS, and push notifications. With Courier, businesses can deliver messages to their users using their preferred communication channels, with real-time visibility and tracking for each message. This tracked data within Courier can now be passed to RudderStack!&lt;/p&gt;

&lt;p&gt;One of the best things about Courier is that it provides a single, unified interface for developers to manage all communications. This reduces complexity and eliminates the need for developers to manage multiple APIs and libraries to send notifications to various channels. Courier's simple and elegant APIs make it easy for developers to create new notifications and manage messages in a way that doesn’t require additional work for each new notification channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  More about RudderStack
&lt;/h2&gt;

&lt;p&gt;RudderStack, as we already mentioned, is an easy-to-use CDP, with a straightforward installation process and intuitive user interface. It doesn’t require any in-depth technical knowledge, making it a popular choice for businesses that want to collect and process customer data without the need for a dedicated development team.&lt;/p&gt;

&lt;p&gt;Unlike most other CDPs, RudderStack is open-source, which means that developers can access the source code, modify it to suit their needs, and contribute to the development of the platform. Additionally, RudderStack offers a range of pricing plans, including a free plan, making it accessible to businesses of all sizes. Another great feature of RudderStack is that you can host it in your private cloud infrastructure, giving you even more control over your data.&lt;/p&gt;

&lt;p&gt;Unlike most other CDPs, RudderStack is open-source, which means that developers can access the source code, modify it to suit their needs, and contribute to the development of the platform. Additionally, RudderStack offers a range of pricing plans, including a free plan, making it accessible to businesses of all sizes. Another great feature of RudderStack is that you can host it in your private cloud infrastructure, giving you even more control over your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  RudderStack’s control plane
&lt;/h2&gt;

&lt;p&gt;The RudderStack platform consists of two main components: the user-facing control plane and the data plane that operates under the hood. In this section, we'll take a closer look at the control plane.&lt;/p&gt;

&lt;p&gt;The control plane is the web-based interface that allows you to manage data integrations, monitor data flows, and control user access. It's the place where you can configure and manage your RudderStack accounts, add and remove integrations, and control who can access data.&lt;/p&gt;

&lt;p&gt;RudderStack’s control plane also lets you manage your data integrations easily. For instance, you can set up and choose your data destinations and sources, control who can access the data, and monitor the flow of data between the sources and destinations. &lt;/p&gt;

&lt;p&gt;In RudderStack, &lt;a href="https://www.rudderstack.com/docs/sources/overview/"&gt;sources&lt;/a&gt; are platforms or applications from which RudderStack tracks and collects data. A &lt;a href="https://www.rudderstack.com/docs/dashboard-guides/destinations/"&gt;destination&lt;/a&gt;, on the other hand, is a cloud tool or a platform where you want to send this collected data.&lt;/p&gt;

&lt;p&gt;From the control plane, you can also create custom transformations and enrichments to modify the data before it gets sent to the destination. These features provide you with granular control over your data integrations and allow you to create custom data pipelines that fit your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Events — the data sent by Courier
&lt;/h2&gt;

&lt;p&gt;There are two types of user data Courier can now send to RudderStack: message events and audience events.&lt;/p&gt;

&lt;p&gt;Message events are all about notification deliverability and user engagement. These events track whether a message was sent, delivered, clicked, and opened. Message events help businesses track notification usage data, including successful deliveries, opens, clicks, and the channels through which messages are sent. This data can help businesses create custom user journeys that are tailored to users' communication preferences, improving the overall user experience.&lt;/p&gt;

&lt;p&gt;Audience events, on the other hand, make use of Courier's dynamically defined user groups, known as audiences. Audiences give you more flexibility than static lists. For instance, you can define an audience as a set of users with the title “Software Engineer.” You can even take this one step further and define more niche audiences by specifying that the user also has listed, for example, TypeScript as one of their favorite programming languages. &lt;/p&gt;

&lt;p&gt;Audience events allow you to track when people join or leave these pre-defined groups based on changes in user characteristics, allowing for better data collection and therefore improved end user experience. For example, an audience event would be triggered if someone changed their job title to “Software Engineer” or if they changed it from “Software Engineer” to something else. This gives you control over the type and amount of notifications specific users receive, improving their overall experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using this new data
&lt;/h2&gt;

&lt;p&gt;When your events have been loaded in RudderStack, you can use different aggregate views to study them, or you can forward them to an analytics destination like Looker or Tableau. This integration enables you to perform analytics on your notification data, giving you powerful insights about how your users interact with your product, and create even more enjoyable notification experiences. &lt;/p&gt;

&lt;p&gt;You can also automatically update information in Courier from your product or from other software tools via RudderStack. For example, if a user changes their email address in your web app, you can propagate that information into the user’s profile in Courier without the need to add a separate Courier API request.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are you waiting for?
&lt;/h2&gt;

&lt;p&gt;Whether you're new to RudderStack or an existing user, integrating Courier is a straightforward process. To enable the integration, add Courier as a source and/or destination. Adding Courier as a destination is as simple as specifying Courier as a "destination" in your RudderStack dashboard. Just navigate to ‘&lt;a href="https://app.rudderstack.com/directory"&gt;directory&lt;/a&gt;’, go to the &lt;code&gt;destinations tab&lt;/code&gt; and search for Courier.&lt;/p&gt;

&lt;p&gt;For more detailed information on how to implement courier as a source or destination, and help getting started with the RudderStack and Courier integration, visit our &lt;a href="https://www.courier.com/docs/external-integrations/rudderstack/"&gt;documentation&lt;/a&gt; and follow the steps outlined there. This will start the flow of data and can start giving insights straight away, helping you to improve your user experience and helping you make better product decisions.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Internationalize Your App’s Product Notifications</title>
      <dc:creator>Tejas Kumthekar</dc:creator>
      <pubDate>Thu, 10 Feb 2022 21:12:20 +0000</pubDate>
      <link>https://forem.com/courier/how-to-internationalize-your-apps-product-notifications-43ai</link>
      <guid>https://forem.com/courier/how-to-internationalize-your-apps-product-notifications-43ai</guid>
      <description>&lt;p&gt;Internationalization in software development, known as “i18n” for its number of letters, is as vital as ever for modern companies. Web-based products and services are no longer tied to geographical and cultural boundaries and not every potential customer speaks English. You might have designed your app with your native region in mind, but you’ll eventually need to expand your supported languages and regions to cater to a global audience. &lt;/p&gt;

&lt;p&gt;In this article, you’ll learn about the scope of internationalization and the tools that are available to build software with internationalization logic. You’ll also see how Courier’s own internationalization workflow is designed and our suggestions for how to do it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is internationalization?
&lt;/h2&gt;

&lt;p&gt;The purpose of internationalization is to prepare your product for expansion into other markets and countries. With i18n-ready software,  you can competitively grow your customer base. Increasing supported languages can also provide sustained future growth. Effective internationalization involves abstracting much of the software by removing all culture- and locale-specific elements. These elements include languages, regions, dates, units, currency, and even cultural symbols and colors. An i18n-ready software should be free of all hard-coded region-specific elements. &lt;/p&gt;

&lt;p&gt;Localization, or “L10N,” would then be the process of creating these specific elements for every different locale. This is a recurring process that needs to be completed for every additional locale, while internationalization is done only once to prepare the software to be localized. Localization is more than just translation as it involves updating all cultural markers, such as icons, symbols, colors, etc. Effective localization is dependent upon carefully crafted i18n logic in the software.&lt;/p&gt;

&lt;p&gt;At Courier, we serve clients who increasingly look to reach customers across the world. Read on below to learn how we’ve crafted an internationalization workflow. &lt;/p&gt;

&lt;h2&gt;
  
  
  How does Courier work with i18n flow?
&lt;/h2&gt;

&lt;p&gt;Courier recently introduced a &lt;a href="https://docs.courier.com/docs/checks-system"&gt;checks system&lt;/a&gt; that is similar to GitHub’s checks. We implemented the notifications flow around these checks to allow for maximum flexibility and automation. The diagram below illustrates how the checks system establishes a flow between Courier and you.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/46j9rfkWLW6qGTYgqSvu73/8a90065f762a15fdbd706108330abc57/image1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/46j9rfkWLW6qGTYgqSvu73/8a90065f762a15fdbd706108330abc57/image1.png" alt="internationalization-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, you create the notification in the Courier workspace and then submit it for review. The HTTP webhook listens for this submission event and notification content is received via the API call. You can then initiate translations in your own space. &lt;/p&gt;

&lt;p&gt;The i18n process is emphasized at the bottom of this diagram. As you submit your own content for translation/localization, you can work piecemeal with your translators until the notification template is translated and localized for each desired locale. You can update as you go along, instead of waiting until all language translations have been completed for each notification block. &lt;/p&gt;

&lt;p&gt;As the localization process is completed for each locale, you can notify Courier by updating the check-status via API and Courier will know the notification is ready for publishing. For example, if all German translations are complete but the French ones are not, you can still upload all the German translations and push those through with the checks system while waiting for the French translations. This process allows for a smoother workflow as you can prepare notifications and translations and Courier will take care of the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who handles the translation?
&lt;/h3&gt;

&lt;p&gt;All translation and localization work is done outside of Courier. It’s up to you to find the right localization service for your product. There are many machine translation services available these days but they are not suitable for most cases as localization is much more than translation. Many developers don’t realize how many locale assumptions their system is making. For example, China perceives good or lucky colors differently than many western nations. There, red is actually lucky and associated with positivity while green is unlucky and is even used to denote financial losses. &lt;/p&gt;

&lt;p&gt;Another cultural marker that might easily be missed by a developer is the direction of reading, such as between English which reads from left to right, and Arabic or Hebrew which reads from right to left. A proper localization service would consider gender, word order, abbreviations, and many more such cultural marks for each specific locale.&lt;/p&gt;

&lt;p&gt;So, while it’s up to you to translate and localize your notification content, we set up Courier to handle the communication pathways, the storage of locale-specific translations, and user profiles, and the data logging for your notifications. Basically, Courier has designed a workflow that abstracts all the logic so that you only have to provide your own translations and at your own pace. &lt;/p&gt;

&lt;h2&gt;
  
  
  What would your i18n flow look like without Courier?
&lt;/h2&gt;

&lt;p&gt;If your current software is not already designed with internationalization logic, retrofitting it is usually costly due to the labor and time involved. Every hard-coded locale-specific element will have to be re-coded as a variable that grabs the correct locale for each stored user profile from a library. &lt;/p&gt;

&lt;p&gt;If you want to implement your own i18n flow, here are some recommendations. First, you’ll want to decide on your process and document it. You will need to set up a project management system and ensure a way for the development team to communicate with the localization team. One possibility might be to hire a project manager into the flow. It will be up to the development team to hand off notifications for translation and localization, and upon receipt of the translations from the localization team, add them into the system. For each new notification, this process will need to be cycled through, where a translation is requested and run through the process and then uploaded into the system. &lt;/p&gt;

&lt;p&gt;Depending on the content and complexity of the notification, there might be some other difficulties. Courier uses various drag-and-drop content blocks as the basic components of a notification. If you’re building your system in a similar fashion, you’ll need to ascertain how you will abstract each block so that they are not hard-coded with locale-specific elements. Simple blocks, like text, can be replaced with custom variables. That seems intuitive. However, when you scale your company, this will become more difficult to implement as the different invocations will have to be written per locale. In addition, if your notification composition is complex and includes a variety of content blocks, each block would need to be handled as a custom variable as well. This can quickly become unmanageable if not accounted for from the outset. &lt;/p&gt;

&lt;h3&gt;
  
  
  Are there available resources for implementing i18n yourself?
&lt;/h3&gt;

&lt;p&gt;There are several open-source libraries and frameworks available to develop i18n-ready software. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://www.i18next.com/"&gt;i18next&lt;/a&gt;, a comprehensive open-source framework that has been around since 2011 with a dedicated community of developers. It provides you with plugins that can detect language and load translations, with support for multiple files. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://formatjs.io/"&gt;FormatJS&lt;/a&gt; and &lt;a href="https://github.com/globalizejs/globalize"&gt;GlobalizeJS&lt;/a&gt;, which are collections of javascript libraries for i18n formatting and other tools. However, unlike i18next, developers using FormatJS will need to write their own code to detect language. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://guides.rubyonrails.org/i18n.html"&gt;Ruby on Rails&lt;/a&gt; has its own i18n library to abstract locale-specific information, replace it with another locale, and store these dictionaries. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://airbnb.io/polyglot.js/"&gt;Polyglot.js&lt;/a&gt;, which is an interpolation and pluralization helper created by Airbnb.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite the number of tools available, you will still need to ascertain which ones are right for you. As you measure your options, keep in mind how long it might take to develop your i18n flow. Your team will need to write the code that will tie these libraries together. You will also need to configure how to store and manage locales in profiles. And of course, unless you provide the translators access to your codebase, you will still need to devise a method for continuous bi-directional communication between the developers and translators for the duration of the process. &lt;/p&gt;

&lt;h2&gt;
  
  
  Build your i18n workflow with Courier
&lt;/h2&gt;

&lt;p&gt;Courier has introduced an i18n workflow so that you can tap into new markets and locales when you’re ready to expand. We store your customer’s profiles with locales, we’ve built automated communication pathways for developers and translators, and we engineered content blocks for smoother localization, however you design your notifications.&lt;/p&gt;

&lt;p&gt;You can always build your own notification system with i18n flow, but you’ll also spend valuable time and resources in the process. There is massive upside in focusing on your product and expanding your customer base as smoothly and efficiently as possible. Ultimately, the decision rests on available resources: do you have time to write i18n logic and re-design your product, or are expediency and user engagement your priorities for the moment in time? For more of our content, subscribe below or follow us at &lt;a href="http://twitter.com/trycourier"&gt;@trycourier&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>notifications</category>
      <category>l10n</category>
      <category>internalization</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Hacking PostgreSQL Internals to Deliver Push Notifications</title>
      <dc:creator>Tejas Kumthekar</dc:creator>
      <pubDate>Thu, 20 Jan 2022 23:30:36 +0000</pubDate>
      <link>https://forem.com/courier/hacking-postgresql-internals-to-deliver-push-notifications-3dpd</link>
      <guid>https://forem.com/courier/hacking-postgresql-internals-to-deliver-push-notifications-3dpd</guid>
      <description>&lt;p&gt;PostgreSQL &lt;a href="https://www.postgresql.org/about/news/postgresql-14-released-2318/"&gt;announced&lt;/a&gt; their latest version (PostgreSQL 14) on September 30th, which includes a bunch of features like pipeline API, gathering statistics on replication slots, query parallelism improvements and so on. While the &lt;a href="https://www.postgresql.org/about/"&gt;origin&lt;/a&gt; of PostgreSQL can be traced back to 1986, it has been in active development for the past 30 years. Tons of companies, agnostic of the the type and size, have trusted Postgres over the years and their tagline “The world's most advanced open source relational database” is hardly an overstatement.&lt;/p&gt;

&lt;p&gt;In a typical, non-trivial system, there is more than one database server, and the data is often copied across multiple servers. A fairly common notion in a distributed system, this copying of data across multiple server nodes is known as replication. Replication processes are often abstracted from the consumers of the database, so most of the application layers are transparent to which node serves the data and if you’re an app developer like me, we expect things to “just work”. However, what goes behind the scenes of some of the most sophisticated databases of the world is often a fascinating reading and learning experience. &lt;/p&gt;

&lt;p&gt;In this post, we will start diving into the internals of Postgres to understand how replication works and data integrity is ensured using WAL (Write-Ahead Logging). We will then steer towards interesting concepts like logical decoding and output plugins. Finally, we will start hacking some code to write our own plugin that can send push notifications! I know this sounds a bit contrarian to the common approach of sending notifications from application servers, but hey what's the fun in doing common boring things! Let's dive right in :)&lt;/p&gt;

&lt;p&gt;Replication at a high level is a process of transferring data from a primary server to a replica server. In Postgres lingo, servers can either be primary or stand-by. Primary servers are the ones that send data, while standbys are the receivers of replicated data. In certain settings, standbys can also act as senders. Replica or standby servers can take over if the primary fails which is the crux of how database systems manage fault tolerance.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/umbekjjItC7BvTPRbw7up/4428b903f180b404208738310f9cb949/hacking-postgres-1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/umbekjjItC7BvTPRbw7up/4428b903f180b404208738310f9cb949/hacking-postgres-1.png" alt="hacking-postgres-1"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.enterprisedb.com/postgres-tutorials/postgresql-replication-and-automatic-failover-tutorial#replication"&gt;https://www.enterprisedb.com/postgres-tutorials/postgresql-replication-and-automatic-failover-tutorial#replication&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WAL (Write-Ahead Logging) is the standard method to ensure data integrity in Postgres. Systems are bound to fail and database servers are not excused from that. At the application layer, it's comparatively easier to retry and manage failures without typically resulting in data loss, however, when we go deep in the stack, especially at the data layer, persistence is doubtlessly super critical. When consumers make a write operation to the database, before the database is acted-upon, the changes are written to the server’s file system. This by design ensures data persistence and recovery in the cases of operating system crashes or hardware failures. Consumers are (obviously) abstracted from these internal mechanisms and applications connected to the Database expect things to work out of the box. &lt;/p&gt;

&lt;p&gt;The log entry we talked about above is known as the Write-Ahead Log record while the process is called Write-Ahead Logging. Each record has a Sequence number which is used for checkpointing periodically after logs are synchronized to the database. In cases of system crashes, this checkpoint is used to re-read and synchronize. WAL, being a reliable method for synchronizing or recreating the ordered state of the database, is used for replication across multiple servers. WAL can replicate the data using either a File based approach or a Streaming approach where both approaches have their own pros and cons. Streaming replication, although is typically in an asynchronous mode, can be tuned to be synchronous.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/1lsDwMGtrp3KcJK48HkdNV/e17d361ef1604ad97cdbd07ea6cb8a17/hacking-postgres-2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/1lsDwMGtrp3KcJK48HkdNV/e17d361ef1604ad97cdbd07ea6cb8a17/hacking-postgres-2.png" alt="hacking-postgres-2"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://hevodata.com/learn/postgres-wal-replication/"&gt;https://hevodata.com/learn/postgres-wal-replication/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WAL records being representative of the internal state of the Database system, are not easy to be fed into or understood by an external system/consumer. Logical Decoding to the rescue! “&lt;a href="https://www.postgresql.org/docs/9.4/logicaldecoding-explanation.html"&gt;Logical decoding&lt;/a&gt; is the process of extracting all persistent changes to a database's tables into a coherent, easy to understand format which can be interpreted without detailed knowledge of the database's internal state.” Using logical decoding, replication solutions and auditing can be achieved much easily.&lt;/p&gt;

&lt;p&gt;The following diagram depicts the logical decoding process.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/4LhsYtcGoveivrzRFEmmwd/195ea94dfa863f882d6ff23957f09a12/hacking-postgres-3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/4LhsYtcGoveivrzRFEmmwd/195ea94dfa863f882d6ff23957f09a12/hacking-postgres-3.png" alt="hacking-postgres-3"&gt;&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://techcommunity.microsoft.com/t5/azure-database-for-postgresql/change-data-capture-in-postgres-how-to-use-logical-decoding-and/ba-p/1396421"&gt;https://techcommunity.microsoft.com/t5/azure-database-for-postgresql/change-data-capture-in-postgres-how-to-use-logical-decoding-and/ba-p/1396421&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to enable the logical decoding, you need to make some configuration changes to the Postgres instance -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wal_level = logical # default value is `replica`
max_replication_slots = 1 # good enough for a sample project
max_wal_senders = 1 # default is 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ref: &lt;a href="https://www.postgresql.org/docs/current/runtime-config-replication.html"&gt;https://www.postgresql.org/docs/current/runtime-config-replication.html&lt;/a&gt; and &lt;a href="https://www.postgresql.org/docs/current/runtime-config-wal.html"&gt;https://www.postgresql.org/docs/current/runtime-config-wal.html&lt;/a&gt; for details on tuning the configuration&lt;/p&gt;

&lt;p&gt;Once we have the configuration up and running, record changes are passed to the Output Plugin which does the key step of transformation from the WAL format to the format specified in the plugin (eg. JSON). These changes are made available on the replication slot(s) and consumer applications can receive the stream of updates as and when those occur.&lt;/p&gt;

&lt;p&gt;Some output plugins and consumer apps out there -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/eulerto/wal2json"&gt;wal2json&lt;/a&gt; Output Plugin that converts WAL output to JSON objects [Open Source]&lt;br&gt;
&lt;a href="https://www.postgresql.org/docs/current/app-pgrecvlogical.html"&gt;pg_recvlogical&lt;/a&gt;  Postgres app that can consume update stream [Out-of-the-box with Postgres]&lt;br&gt;
decoderbufs Output Plugin that delivers data as protobuf [Open Source, Used in &lt;a href="https://github.com/debezium/debezium"&gt;Debezium&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;We can write a consumer from scratch or use battle-tested tools built to achieve this at scale. Debezium is one of the most widely used solutions out there. Netflix open sourced their in-house tool for CDC called DBLog - &lt;a href="https://netflixtechblog.com/dblog-a-generic-change-data-capture-framework-69351fb9099b"&gt;https://netflixtechblog.com/dblog-a-generic-change-data-capture-framework-69351fb9099b&lt;/a&gt; is a fantastic read on it.&lt;/p&gt;

&lt;p&gt;What are we building? Lets keep this simple - whenever a new user entry is made to Postgres table, we will consume replication logs and send a welcome email to the user.&lt;/p&gt;

&lt;p&gt;Let’s hack some code! We will be using a Go package &lt;a href="https://github.com/jackc/pglogrepl"&gt;https://github.com/jackc/pglogrepl&lt;/a&gt; which is a Postgres logical replication library.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Follow Logical replication instructions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jackc/pglogrepl"&gt;https://github.com/jackc/pglogrepl&lt;/a&gt; README has step by step instructions to configure logical replication in your local Postgres Instance. &lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Try out the demo in the repo
&lt;/h3&gt;

&lt;p&gt;Give &lt;a href="https://github.com/jackc/pglogrepl/tree/master/example/pglogrepl_demo"&gt;https://github.com/jackc/pglogrepl/tree/master/example/pglogrepl_demo&lt;/a&gt; a try - it demos how the library works under the hood&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Update the demo code to log new email inserts
&lt;/h3&gt;

&lt;p&gt;To keep it simple for this blog post, let's add some code that prints out specific email which was inserted on the new row.&lt;/p&gt;
&lt;h4&gt;
  
  
  A.  Look for inserts and print the WAL data
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if logicalMsg.Type() == 'I' {
   // `I` stands for Insert
   log.Println(string(xld.WALData))
   // this logs the complete entry
   // however it would require a little more cleanup
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For example, a new entry with ID 5 and email &lt;a href="mailto:tejas@courier.com"&gt;tejas@courier.com&lt;/a&gt; shows up as I@&lt;a href="mailto:Nt5ttejas@courier.com"&gt;Nt5ttejas@courier.com&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  B. Clean up the internal WAL representation and derive email
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// find the second column
  walData := xld.WALData[5:]
  pos := bytes.Index(walData, []byte("t"))

  email := walData[pos+1:]
  pos = bytes.Index(email, []byte("t"))

  email = email[pos+1:]
  emailStr := string(email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  C. Send a welcome email to the user
&lt;/h4&gt;

&lt;p&gt;We will be creating a new template by configuring an email integration supported by Courier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;messageID, err := client.Send(context.Background(), "VDPE8SWN1K4BWMP8RJ101YRZTF3J", "user-id", 
courier.SendBody{
    Profile: profile{
       Email: emailStr,
    },
    Data: data{
       Foo: "bar",
    },
})

if err != nil {
  log.Fatalln(err) 
}

log.Println(messageID)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the modified code at &lt;a href="https://github.com/tk26/pglogrepl"&gt;https://github.com/tk26/pglogrepl&lt;/a&gt; Here’s a quick &lt;a href="https://www.loom.com/share/f96c687c4aa648b7822464c1601e17ee"&gt;loom video&lt;/a&gt; of how things work.&lt;/p&gt;

&lt;p&gt;Now that we have configured Postgres to send emails by listening to replication logs, changing it to send a push notification would be as simple as changing a configuration in the Courier Studio.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/3VHoF3ofVDXTXHwXtWKAUg/3f59344b1a44e1c12cbc5500d7c13f9e/hacking-postgres-4.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/3VHoF3ofVDXTXHwXtWKAUg/3f59344b1a44e1c12cbc5500d7c13f9e/hacking-postgres-4.png" alt="hacking-postgres-4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! Reach out to us &lt;a href="https://twitter.com/trycourier"&gt;@trycourier&lt;/a&gt; if you have any questions or comments or you know, just say Hi!&lt;/p&gt;

</description>
      <category>notifications</category>
      <category>postgrsql</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Nodemailer: Send through multiple service providers</title>
      <dc:creator>Tejas Kumthekar</dc:creator>
      <pubDate>Sat, 14 Aug 2021 01:47:50 +0000</pubDate>
      <link>https://forem.com/courier/nodemailer-and-the-saas-paradox-of-choice-5bja</link>
      <guid>https://forem.com/courier/nodemailer-and-the-saas-paradox-of-choice-5bja</guid>
      <description>&lt;p&gt;Early stage companies are constantly evolving their product to fit the market they operate in. They reach customers to keep them engaged using a high magnitude vector that contributes to their success. Architecting the communications strategy for your product thus becomes an important problem to tackle, which in turn can cause second-order effects like having to trade off the speed of product development iterations. The decisions you would have to make along the path can be hard, confusing and prone to change, and the last thing you want to invest precious time and energy in is these non-core features of your product.&lt;/p&gt;

&lt;p&gt;In this blog post, we start by diving into Nodemailer, a module that helps send emails from your Node.js backend, and then we steer our way into writing a transport layer plugin that can help you switch downstream email service providers purely by configuration instead of tedious and sometimes massive code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Projects that have stood the test of time
&lt;/h2&gt;

&lt;p&gt;In the fast growing tech industry, there’s a special place in the hall of fame for projects that have stood the test of time. &lt;a href="https://nodemailer.com/about/"&gt;Nodemailer&lt;/a&gt; is one such project that deserves the well-earned spot. Launched in 2010, not long after Node.js’ inception, Nodemailer has been a go-to dependency for sending email messages for Node.js users. With 14k+ stars and 438k+ usages on &lt;a href="https://github.com/nodemailer/nodemailer"&gt;GitHub&lt;/a&gt; and ~1.48M weekly downloads on NPM as of this writing, Node.js developer community has benefited immensely from the project.&lt;/p&gt;

&lt;p&gt;Apart from a wide variety of features provided by Nodemailer, it comes with zero dependencies and has been evolving with a security-first mindset. While some of us might think how hard it can be to have zero dependencies, let's remember we’re in a Node.js world where there is a package for every little thing you could possibly imagine. Don’t believe it? Brace yourself while looking at &lt;a href="https://www.npmjs.com/package/is-odd"&gt;https://www.npmjs.com/package/is-odd&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/is-even"&gt;https://www.npmjs.com/package/is-even&lt;/a&gt;. Yep that’s right - there’s a dependency that can help determine if a number is odd or even. No more complex arithmetic, right? &lt;/p&gt;

&lt;p&gt;On another note, Nodemailer’s security-first development also deserves a round of applause. Those of you who have had to frequently run audits and rack your brains figuring out how to resolve vulnerabilities would agree. Moreover, considering the scale at which projects like Nodemailer have been adopted, even a single vulnerability can have huge negative impacts on the software of the world. One recent example is a &lt;a href="https://www.securityweek.com/vulnerability-netmask-npm-package-affects-280000-projects"&gt;vulnerability in the netmask npm package&lt;/a&gt; that could potentially play a hand in malware delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensibility and elegant abstractions are the cruxes
&lt;/h2&gt;

&lt;p&gt;Nodemailer is highly extensible via plugins that can operate on the mail object (pre-processing step), the mail stream (processing step) or the transports (sending step). For the scope of this blog post, we’re mostly concerned about the sending step. &lt;a href="https://datatracker.ietf.org/doc/html/rfc5321"&gt;SMTP&lt;/a&gt; is the main transport in Nodemailer for delivering messages - but can be extended using the transport plugin to a different transport mechanism. For example, if you already use &lt;a href="https://aws.amazon.com/ses/"&gt;Amazon SES&lt;/a&gt;, you can use the SES transport plugin instead of SMTP, which is built-in. Like SES, there are several email service providers out there like SendGrid, Mailgun, Postmark and SparkPost to name a few. In order to cater to the Node.js (and Nodemailer) audience, most of these providers do have a transport plugin of its own should you need to integrate with your tech stack. You can explore through all such plugins with a simple &lt;a href="https://www.npmjs.com/search?q=nodemailer%20transport"&gt;“Nodemailer transport”&lt;/a&gt; search query on the NPM registry.&lt;/p&gt;

&lt;p&gt;A definite question that always springs to mind is why there are so many email service providers out there and how to choose the one that fits best with your communication strategy. Unsurprisingly, such analysis paralysis is fairly ubiquitous in today’s SaaS world. Calling it a SaaS paradox of choice would hardly be an overstatement. Unless you have theoretically infinite resources at your disposal (read: you’re at a giant like Google or Facebook), these choices can slow down the speed of development significantly and don’t bode well with the “move fast and break things” mantra if you have to go back and forth between email service providers and rewrite your backend stack to accommodate the changeset.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.contentful.com/z7iqk1q8njt4/2H94fFdV1jsgw4UVZBHhIv/a9d44c307294b3e2b9b08ac902042415/nodemailer-1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.contentful.com/z7iqk1q8njt4/2H94fFdV1jsgw4UVZBHhIv/a9d44c307294b3e2b9b08ac902042415/nodemailer-1.png" alt="nodemailer-1"&gt;&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://xkcd.com/2224/"&gt;https://xkcd.com/2224/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Classic build vs buy mindset
&lt;/h2&gt;

&lt;p&gt;In one of our blog posts, we talked about the build vs buy mindset and how it makes a difference, especially if you are a startup - read about it &lt;a href="https://www.courier.com/blog/the-three-things-to-never-build-in-your-app/"&gt;here&lt;/a&gt; if you’re curious. The mindset not only makes a difference for your speed of iteration, but also keeps you potentially ahead in the game especially if and when your competitors are adopting buy over build for non-core features of their product. One of the best examples is hosting on AWS vs maintaining on-premises cloud infrastructure - outsourcing cloud hosting to AWS gives you the speed of iteration with no cost of infrastructure maintenance. Imagine all of your competitors are on AWS but you aren’t - it’d certainly make a tremendous negative difference in the long run. While this certainly does not necessarily apply to all the companies out there, it does hold truth for the most. “Disruptive economic events like COVID have caused many people to step back and think about how they want to change strategically. And many have come to the conclusion that they do not want to own and run their own data centers.”, says AWS CFO Brian Olsavsky &lt;a href="https://www.theregister.com/2021/07/30/amazon_q2_2021/"&gt;https://www.theregister.com/2021/07/30/amazon_q2_2021/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An effective communication strategy plays a critical role in maximizing the odds of product success. It's a no-brainer that you have to have a way to reach out to users via Email - but that’s not going to be enough. Customer engagement via text messaging, in-app messaging or direct messaging (think Slack, Whatsapp, MS Teams etc.) has become a necessity in today’s hyper-competitive market. Sooner or later (definitely sooner than you think), having these multiple channels of communication are going to make a difference to your business. As you would scale your product or company, notifications infrastructure has to scale as well in order to handle queuing, scheduling, routing, rendering, and ensuring delivery of messages across multiple channels. Companies like LinkedIn have spent millions to build this infrastructure in-house - depicted quite well in &lt;a href="https://engineering.linkedin.com/blog/2018/03/air-traffic-controller--member-first-notifications-at-linkedin"&gt;LinkedIn’s Air Traffic Controller&lt;/a&gt; blog post.&lt;/p&gt;

&lt;p&gt;Continuing my mini-rant on the SaaS analysis paralysis, as you’d have probably seen it coming, the paralysis exists on text messaging too - Twilio, Sinch, Plivo and Vonage to name a few providers in the arena. Managing multiple channels and providers to suit the communication strategy you’ve embraced is not a cakewalk. Lets circle back to the never-ending list of email providers and their corresponding Nodemailer transport plugin implementations out there. An easy way to fight the paradox is with a layer of abstraction, such that you can change providers without having to rewrite the code to switch the transport plugins. That’s exactly where Courier comes into picture with its API layer of abstraction and a configurable UI to switch email providers without having to change the code on your end. Needless to say, Courier should expose its own Nodemailer transport plugin which abstracts using a specific email provider plugin. Having this abstraction would imply no vendor lock-in as far as downstream delivery is concerned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the code!
&lt;/h2&gt;

&lt;p&gt;Let’s dive right into writing the plugin! &lt;a href="https://nodemailer.com/plugins/create/"&gt;Nodemailer plugin creation&lt;/a&gt; (specifically the transport section) and a &lt;a href="https://github.com/orliesaurus/nodemailer-mailgun-transport"&gt;Mailgun implementation&lt;/a&gt; would be our reference points. While the core documentation is exhaustive, we’ll keep it relatively simple for the first version of our plugin.&lt;/p&gt;

&lt;p&gt;TL;DR: &lt;a href="https://github.com/tk26/nodemailer-courier-transport"&gt;https://github.com/tk26/nodemailer-courier-transport&lt;/a&gt; is the code we’ll end up writing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 - Create a new NPM package and adding courier-node as a dependency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://www.npmjs.com/package/@trycourier/courier"&gt;Node.js module&lt;/a&gt; for communicating with the Courier REST API. The source code behind the package is MIT licensed at &lt;a href="https://github.com/trycourier/courier-node"&gt;courier-node&lt;/a&gt; repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 - Configuration prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You will need to get a Courier API key to get started. You can sign up and create one for free at &lt;a href="https://courier.com"&gt;courier.com&lt;/a&gt;. Courier being a middleware, you’d have to configure an email provider of your choice that’d do the actual delivery of the email to the recipient. Stuck in the paradox of choice? We got you! Here’s an &lt;a href="https://www.courier.com/blog/best-transactional-email-api-service"&gt;article&lt;/a&gt; we published earlier this year that could be helpful in figuring out. Thereafter, you’d have to create a notification template with email as the channel. &lt;a href="https://docs.courier.com/docs/getting-started-sendgrid"&gt;Here’s&lt;/a&gt; how to do it using SendGrid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 - Writing the transport core&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Transports have a &lt;strong&gt;send()&lt;/strong&gt; method and &lt;strong&gt;name&lt;/strong&gt; and &lt;strong&gt;version&lt;/strong&gt; properties. Transport object gets passed to the &lt;strong&gt;createTransport()&lt;/strong&gt; method to create the &lt;em&gt;transporter&lt;/em&gt; object.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const transport = (options) =&amp;gt; {
  const courier = CourierClient({ authorizationToken: options.apiKey });

  const courierSend = input =&amp;gt; courier.send({
    eventId: input.eventId,
    recipientId: input.userId,
    profile: {
    email: input.to,
    },
  });

  return {
    name: "Courier",
    version: packageData.version,
    send: send(courierSend),
  };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That’s pretty much it for the basic version of the plugin. We’ve pushed the code to &lt;a href="https://github.com/tk26/nodemailer-courier-transport"&gt;https://github.com/tk26/nodemailer-courier-transport&lt;/a&gt;. feel free to play around with it and reach out with any questions/feedback. It’s open sourced under MIT license so feel free to contribute to the open source project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 - Using the code in your tech stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s how things would look in your Node.js backend -&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const nodemailer = require("nodemailer");
const courierTransport = require("nodemailer-courier-transport");

// This is your API key that you retrieve from courier.com account (free up to 10k monthly sends)
const auth = {
  apiKey: "ABCDEFGHIJKLMN",
};

const nodemailerCourier = nodemailer.createTransport(courierTransport(auth));

nodemailerCourier.sendMail(
  {
    eventId: "courier-event-id", // Configured in Courier UI
    to: "recipient@domain.com",
    userId: "unique-user-id",
  },
  (err, info) =&amp;gt; {
    if (err) {
    console.log(`Error: ${err}`);
    } else {
    console.log(`Response: ${info}`);
    }
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note: We haven’t published the package yet as it is still in a nascent stage. Let us know if you are looking to contribute to the package or want us to publish it so you can use it in your backend. :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;Let's summarize the key takeaways from the post. First and foremost - writing and maintaining open source projects that stand the test of time is hard, and projects like Nodemailer are great examples to learn from. Buying over building non-core product features is one of the most important trade-offs you can do to grow your company faster. The sooner you do the trade-off, the higher the returns (and in turn the competitive advantage) you gain. Last but not least, communication strategy is a key pillar in your company’s success - make sure you get it right without compromising on the speed of iteration.&lt;/p&gt;

&lt;p&gt;If you’re looking for an easy to integrate, scalable and a reliable communication strategy - sign up for a free &lt;a href="https://www.courier.com/"&gt;Courier&lt;/a&gt; account. You build the next big thing and let us handle the communications for you! 🚀&lt;/p&gt;

</description>
      <category>notifications</category>
      <category>api</category>
    </item>
    <item>
      <title>The Three Things to Never Build In Your App: Authentication, Notifications, and Payments</title>
      <dc:creator>Tejas Kumthekar</dc:creator>
      <pubDate>Tue, 18 May 2021 00:30:07 +0000</pubDate>
      <link>https://forem.com/courier/the-three-things-to-never-build-in-your-app-authentication-notifications-and-payments-53m4</link>
      <guid>https://forem.com/courier/the-three-things-to-never-build-in-your-app-authentication-notifications-and-payments-53m4</guid>
      <description>&lt;p&gt;Author: Tejas Kumthekar&lt;/p&gt;

&lt;p&gt;Back in early 2018, I embarked on a side gig with a few partners - the idea was to make ridesharing socially engaging and fun. We made a ton of mistakes and never really got the product off the launchpad, however in retrospect, the biggest mistakes we made were wasting precious time in writing code for authentication and authorization as well as user notifications. We learned to focus on customer value the hard way. &lt;/p&gt;

&lt;p&gt;Every startup should consider the trade-offs of buying vs building non-differentiated features like authentication, notifications, payments etc. Companies like &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; (authentication and authorization platform), &lt;a href="https://www.courier.com/" rel="noopener noreferrer"&gt;Courier&lt;/a&gt; (one API to design and deliver notifications across multiple channels), and &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; (payments infrastructure for the internet) have solved these problems so you, the entrepreneur or developer, can stay laser-focused on what your users truly want out of your product.&lt;/p&gt;

&lt;p&gt;Whether you are working on a side gig like me or starting a company, shipping an MVP as quickly as possible helps you get feedback much earlier in the product development cycle. Outsourcing authentication, notifications, and payments tremendously helps in rolling your MVP out the door. The project I mentioned above took months to reach the MVP stage and outsourcing the non-core features could have had a great impact on shortening that time frame.&lt;/p&gt;

&lt;p&gt;In this post, we shall focus on a pretty common use case that suits almost every product you will ever build. A user signs up for your product, you save the user details, and send them a welcome message. We will be using Auth0’s post-user registration hook and Courier’s automations feature to build this use case fairly quickly. Let’s dive right in!&lt;/p&gt;

&lt;p&gt;With a single “send” API invocation, you can use Courier to reach your customers across one or more channels (email, text, push, or a direct message). We learned through talking to our customers that more often than not, they need something more powerful than just a single “send” action. They need workflows that do multiple actions with a single invocation, like making a single API call to send an email to their users and wait for a desired period of time (inducing a delay) and thereafter send a follow-up email. Another example of a workflow we've heard quite a lot is scheduled notifications, like sending product updates every week. &lt;a href="https://help.courier.com/en/articles/5129279-how-to-build-and-trigger-multi-step-notification-automations-to-users" rel="noopener noreferrer"&gt;The Courier Automations API&lt;/a&gt; gives you the ability to build such workflows and quite literally “automate” your notifications. We will see it in action soon :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Courier side of things
&lt;/h2&gt;

&lt;p&gt;Let's start by creating a free account on Courier, which lets you send up to 10,000 free notifications every month. For the use case we are tackling in this blog post, we will be leveraging Courier’s Automations API. With a single API call, you’ll be able to create a new user profile inside Courier followed by a welcome message to send to this newly created user. The Automations Guide has a detailed walkthrough of all the cool features unlocked via Courier’s Automations API. Today, we will be invoking an automation that has two steps - update-profile and send.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: update-profile
&lt;/h3&gt;

&lt;p&gt;This step essentially takes any profile JSON object with a unique ID (called recipient ID) and saves it inside Courier. It stays as a permanent record inside Courier that you can further use to send notifications, add it to a list of users/profiles, customize preferences such as opt-in/opt-out, and so on. In our use case, this would originate from Auth0. Here’s how it would look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update-profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipient_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;RECIPIENT_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// User properties from Auth0&lt;/span&gt;
     &lt;span class="c1"&gt;// plus any more properties you want to attach to the user&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Save Profile Information as JSON Object in Courier&lt;/em&gt;&lt;/p&gt;

&lt;p&gt; =  A unique ID that you can use to identify your user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: send
&lt;/h3&gt;

&lt;p&gt;This step sends a welcome email to the user. We will be sending users an email using Sendgrid as the downstream email provider. This &lt;a href="https://docs.courier.com/docs/getting-started-sendgrid" rel="noopener noreferrer"&gt;Setup Email using SendGrid&lt;/a&gt; guide walks you through sending an email notification using Courier and SendGrid. Once you have created a notification template (with email as the channel using SendGrid), you can construct a send step that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo@bar.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;RECIPIENT_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;TEMPLATE_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Construct a send Step&lt;/em&gt;&lt;/p&gt;

&lt;p&gt; = User ID you are sending the notification to (same as step 1)&lt;/p&gt;

&lt;p&gt; = ID of the notification template you just created for welcoming the new user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Payload
&lt;/h3&gt;

&lt;p&gt;Here’s how the payload to automations/invoke endpoint will look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;automation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;steps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update-profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipient_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unique-foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
                 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo@bar.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;first_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;last_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
             &lt;span class="p"&gt;},&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
                 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo@bar.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
             &lt;span class="p"&gt;},&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AAZRHC4TDAMEHKGJHDAA9199WMVH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unique-foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Payload to Automations&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Auth0 side of things
&lt;/h2&gt;

&lt;p&gt;This post assumes you have a basic understanding of Auth0 and have set up an application in Auth0 before, but if not, follow the &lt;a href="https://auth0.com/docs/get-started" rel="noopener noreferrer"&gt;Auth0 Getting Started&lt;/a&gt; guide. &lt;/p&gt;

&lt;p&gt;Let's take a step back and imagine how things would look if there was no integration between Auth0 and Courier. Every time you would have a new user signed up with Auth0, you would have to explicitly invoke Courier to create their profile and send them a welcome message. This would require writing more code in your application, deploying it reliably and maintaining it as your application scales. Having Auth0 and Courier integrated out of the box gives you great leverage in the form of speed and agility to move fast - without having to maintain a single line of code in your infrastructure.&lt;/p&gt;

&lt;p&gt;We want Auth0 + Courier to work together automatically - like magic. Once a new user registers for the product, we want to trigger Courier’s Invoke Automation API - this is where &lt;a href="https://auth0.com/docs/hooks/extensibility-points/post-user-registration" rel="noopener noreferrer"&gt;Auth0 Post-User Registration&lt;/a&gt; comes into picture. Here’s the code that lives behind Auth0 hook and uses the Courier Node.js SDK to invoke the automation dynamically based on the user obtained via Auth0 registration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CourierClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@trycourier/courier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;courier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CourierClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;authorizationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;               &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webtask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COURIER_AUTH_TOKEN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;courier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;automations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeAdHocAutomation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;automation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;steps&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="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update-profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phoneNumber&lt;/span&gt;
         &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="na"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
           &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
         &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AAZRHC4TDAMEHKGJHDAA9199WMVH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Invoke the Automation Dynamically&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This saves the user profile to Courier followed by sending the welcome email you configured in the template. &lt;/p&gt;

&lt;p&gt;The profiles can be viewed in Courier UI under Data Recipients tab:&lt;br&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%2Fpuhb02ufezvn41f58y4k.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%2Fpuhb02ufezvn41f58y4k.png" alt="view-profiles-in-courier-UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, the welcome email is sent to the user - it looks just like how you configured it to look in Courier. Just to give an example of how a notification could look:&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%2Fb1fcvbx825ijocskypj3.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%2Fb1fcvbx825ijocskypj3.png" alt="welcome-to-courier-email"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feel the Magic Automation?
&lt;/h2&gt;

&lt;p&gt;We just saw how Auth0 + Courier can help you automagically build user onboarding flow without having to maintain any code in your own system, and as we all know, the best code is no code that allows us to focus our energies where they are most needed.&lt;/p&gt;

&lt;p&gt;Of course, Courier Integrates with more than just Auth0. Just like we sent an email using Sendgrid in this blog post, you can use any of these channels - email, SMS, push, direct messaging, etc. with providers like Mailgun, Twilio, and Slack with just a single API to power your notification workflows. Sign up for a free Courier account. You build the next big thing and let us handle the notifications for you. 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional References -
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.courier.com/docs" rel="noopener noreferrer"&gt;Courier Docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://auth0.com/docs/" rel="noopener noreferrer"&gt;Auth0 docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/trycourier/courier-node" rel="noopener noreferrer"&gt;Courier Node.js SDK&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildvsbuy</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Three Things to Never Build In Your App: Authentication, Notifications, and Payments</title>
      <dc:creator>Tejas Kumthekar</dc:creator>
      <pubDate>Fri, 14 May 2021 00:54:01 +0000</pubDate>
      <link>https://forem.com/tk26/the-three-things-to-never-build-in-your-app-authentication-notifications-and-payments-1167</link>
      <guid>https://forem.com/tk26/the-three-things-to-never-build-in-your-app-authentication-notifications-and-payments-1167</guid>
      <description>&lt;p&gt;&lt;a href="https://www.courier.com/blog/the-three-things-to-never-build-in-your-app"&gt;https://www.courier.com/blog/the-three-things-to-never-build-in-your-app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>startup</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
