<?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: Oleksii Klochai</title>
    <description>The latest articles on Forem by Oleksii Klochai (@chief_wizard).</description>
    <link>https://forem.com/chief_wizard</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%2F761164%2Fa12aa55f-8a34-4174-9e7d-63aff992aad2.JPG</url>
      <title>Forem: Oleksii Klochai</title>
      <link>https://forem.com/chief_wizard</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/chief_wizard"/>
    <language>en</language>
    <item>
      <title>Sending Scheduled and Recurring Email Notifications with PHP</title>
      <dc:creator>Oleksii Klochai</dc:creator>
      <pubDate>Thu, 27 Jul 2023 11:01:32 +0000</pubDate>
      <link>https://forem.com/courier/sending-scheduled-and-recurring-email-notifications-with-php-28n3</link>
      <guid>https://forem.com/courier/sending-scheduled-and-recurring-email-notifications-with-php-28n3</guid>
      <description>&lt;p&gt;Sending timely targeted email notifications greatly affects how your audience engages with your product. For these notifications to be effective at notifying your users of key events, you need to schedule when they are sent so that they are delivered at the right time. &lt;/p&gt;

&lt;p&gt;Scheduled and recurring notifications are in use everywhere — for example, online stores use scheduled notifications to inform users about sale events (like Black Friday), and doctors, dentists, and tradespeople have systems that send appointment reminders. Recurring emails are commonly used for subscription services to email monthly bills to customers.&lt;/p&gt;

&lt;p&gt;This tutorial covers two different ways for PHP developers to send scheduled and recurring email notifications through the Courier notification platform using its &lt;a href="https://github.com/trycourier/courier-php"&gt;PHP SDK&lt;/a&gt;. It also offers a low-code solution for sending scheduled emails using just the Courier UI. Courier is a multi-channel notification service with a robust API, which you can use to build a production-ready email notification system in a few minutes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Configure an email service provider in Courier
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://app.courier.com/signup"&gt;Create a Courier account&lt;/a&gt; if you don’t already have one, so that you can configure an &lt;a href="https://www.courier.com/integrations/#email"&gt;email service provider&lt;/a&gt; in Courier, allowing it to send emails on your behalf. \&lt;/p&gt;

&lt;p&gt;In the Courier app, navigate to &lt;strong&gt;&lt;a href="https://app.courier.com/channels"&gt;Channels&lt;/a&gt;&lt;/strong&gt; and choose your email service provider. For this tutorial, we will use Gmail.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/35bXpb6BEkYc4bTFRefipC/43181e7401688bd42ead6034633a3f10/send_notifications_php_1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/35bXpb6BEkYc4bTFRefipC/43181e7401688bd42ead6034633a3f10/send_notifications_php_1.png" alt="Screenshot showing how to select an email service provider in Courier."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, select &lt;strong&gt;Sign in with Google&lt;/strong&gt; to give Courier permission to access your Gmail account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a notification template in Courier
&lt;/h2&gt;

&lt;p&gt;Courier uses email templates to make it easy to reuse your emails. In this tutorial, we will use an example of a hairdressing business that sends emails to its clients before (and sometimes after) appointments.&lt;/p&gt;

&lt;p&gt;To create your first template, start by navigating to the &lt;a href="https://app.courier.com/designer/notifications"&gt;Designer&lt;/a&gt;. Click &lt;strong&gt;Create Template&lt;/strong&gt;, give it the name &lt;strong&gt;Hair appointment reminder&lt;/strong&gt;, and click &lt;strong&gt;Create Template&lt;/strong&gt; again.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/377LkgHFun4nR5fdlNhpho/102549fcd4df4b60b8e38d91d9f35a57/send_notifications_php_2.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/377LkgHFun4nR5fdlNhpho/102549fcd4df4b60b8e38d91d9f35a57/send_notifications_php_2.gif" alt="Animation showing how to create a notification template in Courier."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, select &lt;strong&gt;Email&lt;/strong&gt; as the channel for this notification (choosing Gmail as the specific provider in the drop-down box). Now, click on your new email channel on the left side to see the no-code editor for designing your notification. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/T3BYkDx6VuzYvN27Hy1dp/1b290705838cdfba4288d87a008d3766/send_notifications_php_3.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/T3BYkDx6VuzYvN27Hy1dp/1b290705838cdfba4288d87a008d3766/send_notifications_php_3.gif" alt="Animation showing how to add text to an email template in Courier and choose Gmail as your notification provider."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your notification the subject “Hair appointment reminder”, and paste the following content into the message body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dear {name},

This email is to confirm your upcoming hair appointment tomorrow.

We look forward to seeing you at {place}. Please arrive five minutes before your appointment start time.

If you need to reschedule, please let us know at least 24 hours prior to your appointment time.

Thank you for your appointment.

The {salonName} Team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The curly braces in this template are variables that will later be passed to the template as data. Now that your template is complete, click &lt;strong&gt;Publish Changes&lt;/strong&gt;. Later on, you will need your notification template ID and Courier authorization token when you want to call the Courier API. These are both available in your notification template settings. Navigate there now and copy both so that you can use them later.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/5npucjyNygwXbMdsslJ69w/0757d2036423c57a073758b7a9b4ebfb/send_notifications_php_4.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/5npucjyNygwXbMdsslJ69w/0757d2036423c57a073758b7a9b4ebfb/send_notifications_php_4.gif" alt="Animation showing how to copy the notification template ID and Courier authorization token in your notification settings."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you have three options: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Directly send scheduled or recurring emails using Courie&lt;/strong&gt;r*&lt;em&gt;:&lt;/em&gt;* Call the &lt;code&gt;sendEnhancedNotification()&lt;/code&gt; function from the Courier PHP SDK, and use a third party task scheduling library called Crunz to deal with the scheduling side of things. This works using cron syntax, so the same principle can be used for scheduled or recurring emails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Courier’s automations to add send logic to your scheduled emails:&lt;/strong&gt; An automation in Courier is a way of chaining together different steps such as the sending of emails (or other notification-related logic) so that the steps happen in a particular order. An automation can be run by calling the &lt;code&gt;invokeAutomation()&lt;/code&gt;function,  and as with option 1, you can use Crunz to deal with the scheduling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using Courier’s no-code automations designer:&lt;/strong&gt; This is a no-code GUI tool in the Courier UI that uses a drag-and-drop canvas to build up your notification logic. It contains some more advanced logic than option 2 (such as the ability to create  email digests or batching).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Option 1: directly send a scheduled or recurring email using Courier
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preparing your PHP environment
&lt;/h3&gt;

&lt;p&gt;For both of the PHP examples shown below, you will need to prepare a PHP environment and create a notification template in the Courier app.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the latest versions of PHP and &lt;a href="https://getcomposer.org/download/"&gt;Composer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Install the following packages using Composer:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/trycourier/courier-php"&gt;trycourier/courier&lt;/a&gt; — the Courier PHP SDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/guzzle/guzzle"&gt;guzzlehttp/guzzle&lt;/a&gt; — required by the Courier PHP SDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/crunzphp/crunz"&gt;crunzphp/crunz&lt;/a&gt; — for scheduling one-time and recurring tasks&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vlucas/phpdotenv"&gt;vlucas/phpdotenv&lt;/a&gt; — for handling environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can install these by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require crunzphp/crunz vlucas/phpdotenv trycourier/courier guzzlehttp/guzzle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the code shown in this tutorial in our working &lt;a href="https://github.com/trycourier/courier-cron"&gt;example repository on GitHub&lt;/a&gt;. If you are cloning the repository, you will need to run the &lt;code&gt;composer install&lt;/code&gt; command to install the dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending a scheduled email
&lt;/h3&gt;

&lt;p&gt;To manage the scheduling side of things, we will use &lt;a href="https://github.com/crunzphp/crunz"&gt;Crunz&lt;/a&gt; — a PHP package that allows you to run scheduled tasks within your PHP code without having to create a cron job for each of them. We will wrap this around Courier’s &lt;code&gt;sendEnhancedNotification()&lt;/code&gt; function, which is used for sending emails. &lt;/p&gt;

&lt;p&gt;For this example, we have specified that the hair appointment reminder should be sent at a specific time (13:30 2023-07-01). However, note that Crunz’s &lt;code&gt;on()&lt;/code&gt; function accepts any date format parsed by PHP's &lt;a href="http://php.net/manual/en/function.strtotime.php"&gt;&lt;code&gt;strtotime()&lt;/code&gt;&lt;/a&gt; function, so it’s easy to pass in a time value derived from one of your variables.&lt;/p&gt;

&lt;p&gt;As per the example in our GitHub repository, you must provide your configuration in an &lt;a href="https://github.com/vlucas/phpdotenv"&gt;&lt;code&gt;.env&lt;/code&gt; file at the project root&lt;/a&gt;. You can create this file by copying the provided example file into your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, fill out the contents of the newly copied &lt;code&gt;.env&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;COURIER_AUTHORIZATION_TOKEN&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Courier API key, found in Settings&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TEST_EMAIL_TO&lt;/td&gt;
&lt;td&gt;Use your own email address as the value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TEST_DIRECT_SEND_NOTIFICATION_TEMPLATE_ID&lt;/td&gt;
&lt;td&gt;The notification template ID for the “Hair appointment reminder” email template&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Crunz requires you to add a single &lt;a href="https://linuxhint.com/cron_jobs_complete_beginners_tutorial/"&gt;crontab&lt;/a&gt; entry that runs every minute. Whenever this job runs, any tasks scheduled using Crunz for that time will be executed. You only need to do this once for your project. Append the following line to your own user’s crontab on your PHP server (you can do this by typing &lt;code&gt;crontab -e&lt;/code&gt; in your terminal):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* * * * * cd /path/to/project &amp;amp;&amp;amp; vendor/bin/crunz schedule:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/path/to/project&lt;/code&gt; with the absolute path to your PHP project. For security reasons, you should not add this line to your root user's crontab but instead create a user who has execute permissions for the project directory or is a member of the group that your web server runs under (by default, &lt;code&gt;www-data&lt;/code&gt; on most Linux systems).&lt;/p&gt;

&lt;p&gt;Crunz requires the presence of a configuration file, which contains a configured timezone. Create this by running this command in your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vendor/bin/crunz publish:config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the default configured timezone is UTC, but you can change this in the config if needed.&lt;/p&gt;

&lt;p&gt;All Crunz tasks must be contained in a directory called &lt;code&gt;tasks&lt;/code&gt; at the root level of your project. The file containing each task should end with &lt;code&gt;Tasks.php&lt;/code&gt;. Create this directory, and inside it, create a file called &lt;code&gt;scheduledSendTasks.php&lt;/code&gt;and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Crunz\Schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Courier\CourierClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Dotenv\Dotenv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Configure environment variables - set the .env directory to the parent of the Tasks directory&lt;/span&gt;
&lt;span class="c1"&gt;// Environment variables are stored in a variable so that they can be passed to the scheduled task, which will not have access to the current global namespace&lt;/span&gt;
&lt;span class="nv"&gt;$dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dotenv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createArrayBacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"/.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure scheduler&lt;/span&gt;
&lt;span class="nv"&gt;$schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schedule&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the Courier PHP SDK - note the first null is the API path, of which we will use the default&lt;/span&gt;
&lt;span class="nv"&gt;$courier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CourierClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'COURIER_AUTHORIZATION_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new scheduled task&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$courier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Send notification using the Courier PHP SDK&lt;/span&gt;
    &lt;span class="nv"&gt;$notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"email"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_EMAIL_TO'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"template"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_DIRECT_SEND_NOTIFICATION_TEMPLATE_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"routing"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"method"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"single"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"channels"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"place"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"123 High Street"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"salonName"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Cutting Edge"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$courier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sendEnhancedNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$notification&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Schedule a single notification for a specific time&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'13:30 2023-07-01'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sending scheduled email'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you’ve created a scheduled task, it will run as soon as the scheduled time is reached. Remember, as mentioned above, the default timezone is UTC, but you can change this.&lt;/p&gt;

&lt;p&gt;If you want to test your scheduled task, you can force it to run immediately using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vendor/bin/crunz schedule:run --force
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will run all scheduled or recurring tasks that you’ve created. If you want to be sure which tasks will run with this command, you can run another command to check how many scheduled tasks you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vendor/bin/crunz schedule:list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output a table containing your scheduled tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---+------------------------------+-------------+-----------------+
| # | Task                         | Expression  | Command to Run  |
+---+------------------------------+-------------+-----------------+
| 1 | Sending scheduled email      | 30 13 1 7 * | object(Closure) |
| 2 | Sending recurring email      | 30 13 * * * | object(Closure) |
| 3 | Sending scheduled automation | 30 13 1 7 * | object(Closure) |
+---+------------------------------+-------------+-----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sending a recurring email
&lt;/h3&gt;

&lt;p&gt;You can also use Crunz to create recurring tasks on a schedule.&lt;/p&gt;

&lt;p&gt;Inside your &lt;code&gt;tasks&lt;/code&gt; directory, create a PHP file called &lt;code&gt;recurringSendTasks.php&lt;/code&gt; and paste this code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Crunz\Schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Courier\CourierClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Dotenv\Dotenv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Configure environment variables - set the .env directory to the parent of the Tasks directory&lt;/span&gt;
&lt;span class="c1"&gt;// Environment variables are stored in a variable so that they can be passed to the scheduled task, which will not have access to the current global namespace&lt;/span&gt;
&lt;span class="nv"&gt;$dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dotenv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createArrayBacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"/.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure scheduler&lt;/span&gt;
&lt;span class="nv"&gt;$schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schedule&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the Courier PHP SDK - note the first null is the API path, of which we will use the default&lt;/span&gt;
&lt;span class="nv"&gt;$courier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CourierClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'COURIER_AUTHORIZATION_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new scheduled task&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$courier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Send notification using the Courier PHP SDK&lt;/span&gt;
    &lt;span class="nv"&gt;$notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"to"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"email"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_EMAIL_TO'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"template"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_DIRECT_SEND_NOTIFICATION_TEMPLATE_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"routing"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"method"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"single"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"channels"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"place"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"123 High Street"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"salonName"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Cutting Edge"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$courier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sendEnhancedNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$notification&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Set up a recurring task&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'13:30'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sending recurring email'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this, again run &lt;code&gt;vendor/bin/crunz schedule:run --force&lt;/code&gt;. However, you’ll only receive one email, as you are using the &lt;code&gt;–-force&lt;/code&gt; option, which forces a single run of each task. When your recurring task is being invoked by Crunz as a scheduled task, it will be called at the specified interval, and if you use the above code example, you will receive one email per day at 13:30.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Laravel? It’s already got scheduling baked in
&lt;/h3&gt;

&lt;p&gt;If you’re using Laravel, you don’t need to worry about setting up your own scheduling solution, as it already has its own(&lt;a href="https://laravel.com/docs/10.x/scheduling"&gt;https://laravel.com/docs/10.x/scheduling&lt;/a&gt;) (and &lt;a href="https://laravel.com/docs/10.x/queues"&gt;queueing&lt;/a&gt;!) built in — one of the many advantages of using a PHP framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: use Courier’s automations to add send logic to your scheduled emails
&lt;/h2&gt;

&lt;p&gt;Sometimes you need to add some logic around the sending of your scheduled or recurring emails, and this is where Courier’s automations can be useful. Automations allow you to chain together a series of &lt;a href="https://www.courier.com/docs/automations/steps/"&gt;different steps&lt;/a&gt;, including the sending of emails based on different notification templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples of when to use automations
&lt;/h3&gt;

&lt;p&gt;Imagine a gym that sends its customers workout tips — it may want to sometimes send out different workout plans based on customers’ age or other groupings. Using automation in Courier, it could implement some basic branching logic such as “if the customer’s age is greater than 50, send an email using the over 50s email template; otherwise, send using the under 50s email template.”&lt;/p&gt;

&lt;p&gt;Another feature that Courier automations offer is the ability to add a delay between two different actions. To reuse our hairdresser example, imagine that in addition to reminding the customer of their appointment the day before, they also want to send an email the day after their appointment to thank them and offer them 10% off their next appointment. Here’s how to implement this using Courier’s automations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending two scheduled emails in one automation
&lt;/h3&gt;

&lt;p&gt;As this involves sending two different emails, you will need to create a second email template for the 10% off offer. Create a new template with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dear {name}, 

We hope you were satisfied with your hair appointment yesterday, and
we would like to offer you 10% off your next booking.

Thanks,

The {salonName} Team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have both email templates ready to go, you can create your automation in Courier. The automation sends the first email, then there is a delay (in the example below, there is a two-minute delay, but for real-world use, you would probably set it to two days), and then it sends a follow-up email. We will continue to use the PHP Crunz package to kick off the automation at the right moment (one day before the customer’s scheduled appointment), meaning that the second follow-up email will be sent one day after their appointment.&lt;/p&gt;

&lt;p&gt;To follow along with this example, you will need to have followed the steps in the “Prepare your PHP environment” step explained earlier.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;tasks&lt;/code&gt; directory in your PHP project, create a file called &lt;code&gt;scheduleAutomationTasks.php&lt;/code&gt; and paste in the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Crunz\Schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Courier\CourierClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Dotenv\Dotenv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Configure environment variables - set the .env directory to the parent of the Tasks directory&lt;/span&gt;
&lt;span class="c1"&gt;// Environment variables are stored in a variable so that they can be passed to the scheduled task, which will not have access to the current global namespace&lt;/span&gt;
&lt;span class="nv"&gt;$dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dotenv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createArrayBacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"/.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure scheduler&lt;/span&gt;
&lt;span class="nv"&gt;$schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schedule&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the Courier PHP SDK - note the first null is the API path, of which we will use the default&lt;/span&gt;
&lt;span class="nv"&gt;$courier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CourierClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'COURIER_AUTHORIZATION_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new scheduled task&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$courier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Invoke an automation using the Courier PHP SDK&lt;/span&gt;
    &lt;span class="nv"&gt;$automation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"steps"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"action"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;"recipient"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_AUTOMATION_RECIPIENT_USER_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;"template"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_1'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Reminder email&lt;/span&gt;
                &lt;span class="s2"&gt;"brand"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_COURIER_BRAND_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="s2"&gt;"salonName"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Cutting Edge"&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="s2"&gt;"action"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"delay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;"duration"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"2 minutes"&lt;/span&gt; &lt;span class="c1"&gt;// You will probably want to delay by days or hours, but minutes are easier for testing&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"action"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;"recipient"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_AUTOMATION_RECIPIENT_USER_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;"template"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_2'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Follow-up email&lt;/span&gt;
                &lt;span class="s2"&gt;"brand"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dotenv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_COURIER_BRAND_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="s2"&gt;"salonName"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Cutting Edge"&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="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$courier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invokeAutomation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$automation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Schedule the automation for a specific time&lt;/span&gt;
&lt;span class="nv"&gt;$task&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'13:30 2023-07-01'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sending scheduled automation'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you’ve updated your &lt;code&gt;.env&lt;/code&gt; file with any configuration you need to run this automation: &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;COURIER_AUTHORIZATION_TOKEN&lt;/th&gt;
&lt;th&gt;Courier API key, found in Settings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TEST_AUTOMATION_RECIPIENT_USER_ID&lt;/td&gt;
&lt;td&gt;Find your user ID in Courier’s list of users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_1&lt;/td&gt;
&lt;td&gt;The notification template ID for the “Hair appointment reminder” email template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TEST_AUTOMATION_NOTIFICATION_TEMPLATE_ID_2&lt;/td&gt;
&lt;td&gt;The notification template ID for the “Hair appointment — 10% off” email template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YOUR_COURIER_BRAND_ID&lt;/td&gt;
&lt;td&gt;Choose your brand and find its ID in its “brand settings” or URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now run &lt;code&gt;vendor/bin/crunz schedule:run --force&lt;/code&gt;, and you will receive the two different emails with the specified delay in between.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic automations API documentation
&lt;/h3&gt;

&lt;p&gt;To understand all the features that automations can offer, you can play around with our dynamic request builder in Courier’s [automation API documentation][20]. This allows you to build up your PHP automation request dynamically by adding request parameters. For example, if you add your preferred Courier brand ID in the “brand” box, your brand ID will be automatically added to a PHP request on the box on the right. You will need to select the “PHP” button to get a PHP request; however, other languages are available at the click of a button. &lt;/p&gt;

&lt;p&gt;One of the key features of a Courier automation is the series of “steps” that make it up. To understand the different steps that can be part of an automation, see Courier’s &lt;a href="https://www.courier.com/docs/automations/steps/"&gt;extensive documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/49yYL3xO9xIGmkiWoXDLEc/e5fdb876c65207a99acd15d978fb8c12/send_notifications_php_6.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/49yYL3xO9xIGmkiWoXDLEc/e5fdb876c65207a99acd15d978fb8c12/send_notifications_php_6.png" alt="How to use the dynamic request builder tool in Courier’s automation API documentation — a convenient visual tool to help you explore the structure of an automation API request object."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: use Courier’s no-code automations designer to build complex logic around scheduled emails
&lt;/h2&gt;

&lt;p&gt;The automations designer is a UI tool for building automation templates in Courier. An automation template offers a way to reuse Courier automations, and because they can be created in the Courier UI, they are super easy to create. Even your non-developer colleagues will be able to create automation templates using Courier’s simple drag-and-drop canvas.&lt;/p&gt;

&lt;p&gt;For this example, we will use a “remember to pay your taxes” email, as this email could be sent on a schedule (April 1 of this year) or as a recurrence (April 1 every year).&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a notification template
&lt;/h3&gt;

&lt;p&gt;Create a new notification template with the subject “Tax deadline approaching,” and use the AI content generator to create some text for the body of your email. This may looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dear {name},

The end-of-year tax deadline is fast approaching. If you haven't yet filed your taxes, you can do this using our app:

{appUrl}

If you have any questions or concerns, please don't hesitate to contact us.

Thank you,

The {companyName} Team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that your template is complete, click &lt;strong&gt;Publish Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/2xzlzffuqUCq8wItTLfT2C/0cfbd0117b0899494aa1762df4ca38d6/send_notifications_php_6-1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/2xzlzffuqUCq8wItTLfT2C/0cfbd0117b0899494aa1762df4ca38d6/send_notifications_php_6-1.png" alt="Screenshot showing how to create an email template in Courier."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an automation template
&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;a href="https://app.courier.com/automations"&gt;automations&lt;/a&gt;, and click &lt;strong&gt;New Automation&lt;/strong&gt;. Rename your automation from “Untitled Automation” to “Tax Deadline Reminder.”&lt;/p&gt;

&lt;p&gt;To define the trigger for your automation, drag the &lt;strong&gt;Schedule&lt;/strong&gt; trigger onto the canvas. &lt;/p&gt;

&lt;p&gt;For a one-off scheduled email, change the &lt;strong&gt;Type&lt;/strong&gt; of the &lt;strong&gt;Schedule&lt;/strong&gt; node to &lt;strong&gt;Date&lt;/strong&gt;, and enter the date and time you want your notification to be sent — in this case, we will choose midnight on April 1, 2024.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/6pBLPEq36P1rP0CmNRw7b9/0421548af718493f8a027d903fda2fd4/send_notifications_php_9.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/6pBLPEq36P1rP0CmNRw7b9/0421548af718493f8a027d903fda2fd4/send_notifications_php_9.png" alt="Screenshot showing how to schedule an email in the Courier automations designer."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a recurring email, change the &lt;strong&gt;Type&lt;/strong&gt; to &lt;strong&gt;Recurrence&lt;/strong&gt;. Then set a start date of April 1, 2024, 00:00, an end date of April 1, 2028, 00:00, and a frequency of &lt;strong&gt;Yearly&lt;/strong&gt;. This will ensure the reminder email is sent for the next five years.&lt;/p&gt;

&lt;p&gt;Next, drag a &lt;strong&gt;Send&lt;/strong&gt; action onto the canvas, and ensure a line connects the bottom of the &lt;strong&gt;Schedule&lt;/strong&gt; node to the top of the &lt;strong&gt;Send&lt;/strong&gt; node so that it’s clear that the send action follows the schedule trigger.&lt;/p&gt;

&lt;p&gt;Enter &lt;code&gt;refs.data.user_id&lt;/code&gt; as the user that the email should be sent to, and select your “Tax deadline approaching” notification template from the drop-down box. Now, click on &lt;strong&gt;Edit&lt;/strong&gt; next to &lt;strong&gt;Advanced&lt;/strong&gt; to edit some advanced properties. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/4fifzJIV2SX4wn6y17GRZc/f9f42eae85623285d04d7e762c3b5ba3/send_notifications_php_10.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/4fifzJIV2SX4wn6y17GRZc/f9f42eae85623285d04d7e762c3b5ba3/send_notifications_php_10.png" alt="Screenshot showing how to edit advanced properties for your email automation."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will use the &lt;strong&gt;Advanced&lt;/strong&gt; area to add some data to send to your automation template. This includes the &lt;code&gt;user_id&lt;/code&gt; referred to in the previous paragraph plus any variables that your &lt;strong&gt;Tax deadline approaching&lt;/strong&gt; notification template may be expecting.&lt;/p&gt;

&lt;p&gt;Add this JSON to the &lt;strong&gt;Data&lt;/strong&gt; section of the &lt;strong&gt;Advanced&lt;/strong&gt; area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "John Doe",
  "user_id": "courier-user-id",
  "appUrl": "example.com/file-taxes",
  "companyName": "Acme Ltd"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/57VkOA1v2pQZjYq754jWkz/30f80cff078cfb88883764a1565aea57/send_notifications_php_11.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/57VkOA1v2pQZjYq754jWkz/30f80cff078cfb88883764a1565aea57/send_notifications_php_11.gif" alt="Screenshot showing how to add the user ID and other variables to your email automation."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click &lt;strong&gt;Publish changes&lt;/strong&gt;. Your email notification will be sent on April 1, 2024, at midnight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending digests on a recurring schedule
&lt;/h2&gt;

&lt;p&gt;If your reason for sending recurring emails is that your users are subscribed to regular news or updates from your app, you may want to set up an email digest. A digest allows you to avoid overwhelming your users with too many emails by condensing a large number of emails into one regular email update. Courier now offers the ability to send email digests. For more information, see the &lt;a href="https://www.courier.com/docs/automations/digest/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Sending scheduled or recurring emails is not the simplest thing to do in PHP, as it doesn't have a task scheduler built into the language. On top of this, using a third party library like Crunz to deal with scheduling includes an extra layer of complication, in that you have to edit your &lt;code&gt;crontab&lt;/code&gt;file to hand over scheduling responsibility from your cron system to Crunz. This requires at least a basic understanding of how cron jobs work, including the syntax needed to run them.&lt;/p&gt;

&lt;p&gt;The simpler option is to remove any need for scheduling tasks in PHP by using Courier's automation designer. This is a no-code solution that brings all your send logic inside the Courier UI. Aside from simplicity, an advantage of this is it allows your non-developer colleagues to be able to review or edit the logic around sending scheduled emails. &lt;/p&gt;

&lt;p&gt;On the other hand, if it's more important to you to keep your send logic locked down and version controlled, and if you're prepared to get to grips with using Crunz, then you just need to decide whether you want to send a simple email (either a single email or a recurring one) — in which case you should use option 1 — or if you need to add more logic to the sending of your notifications, with the possibility of chaining together multiple steps — in which case option 2 is best.&lt;/p&gt;

&lt;p&gt;If you haven’t yet joined Courier, you can &lt;a href="https://app.courier.com/signup"&gt;sign up today&lt;/a&gt; and get started immediately. If you’re an existing Courier user, you can easily try scheduling one of your existing notification templates in our &lt;a href="https://app.courier.com/automations"&gt;automations designer&lt;/a&gt;, or use our PHP SDK to get started with scheduled and recurring emails.&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>notifications</category>
      <category>api</category>
    </item>
    <item>
      <title>A Developer’s Guide to Notification APIs</title>
      <dc:creator>Oleksii Klochai</dc:creator>
      <pubDate>Wed, 26 Jul 2023 13:41:04 +0000</pubDate>
      <link>https://forem.com/courier/a-developers-guide-to-notification-apis-2n90</link>
      <guid>https://forem.com/courier/a-developers-guide-to-notification-apis-2n90</guid>
      <description>&lt;p&gt;While marketing-related notifications are often handled by marketing automation platforms, engineering teams require notification infrastructure that is designed for automated product-driven notifications. This might be a simple &lt;a href="https://www.courier.com/blog/how-to-send-password-resets-via-sms-and-email-using-node-js-and-next-js/"&gt;SMS password reset&lt;/a&gt; notification or new user onboarding email sequence. Or it may be a more complex notification tied to a feature of the application, such as an approval request sequence. &lt;/p&gt;

&lt;p&gt;The infrastructure required to handle these notifications is extensive. Logic is typically required to trigger on an event, follow a specific workflow, personalize content, follow compliance guidelines, respect user preference, and work intelligently across channels (eg. email, SMS, push, in-app inbox, chat, etc). And it may require advanced send logic such as &lt;a href="https://www.courier.com/docs/platform/automations/batching/"&gt;batching&lt;/a&gt;, &lt;a href="https://www.courier.com/docs/platform/automations/digest/"&gt;digesting&lt;/a&gt;, &lt;a href="https://www.courier.com/docs/platform/automations/throttle/"&gt;throttling&lt;/a&gt;, and &lt;a href="https://www.courier.com/docs/platform/automations/scheduling/"&gt;scheduling&lt;/a&gt; of notifications.&lt;/p&gt;

&lt;p&gt;This is precisely what notification APIs solve. A notification API is a combination of backend infrastructure and both server- and client-side SDKs that enable developers to build notifications in web and mobile apps. It's &lt;a href="https://app.courier.com/signup"&gt;free to signup&lt;/a&gt; and try &lt;a href="https://www.courier.com/"&gt;Courier&lt;/a&gt;, the leading Notification API, yourself. Otherwise, read on to learn more.&lt;/p&gt;

&lt;p&gt;Rather than developers spending time and effort building and maintaining the infrastructure on their own, Notification APIs give developers everything they need to create optimized, product-driven notifications that users will open and interact with.&lt;/p&gt;

&lt;p&gt;Notification APIs are used together with communication APIs like Twilio, Messagebird, Slack, and FCS—acting as an abstraction layer to enable notification use cases. As a result, integrating notifications into an application and maintaining that infrastructure becomes a swift and future-proof process.&lt;/p&gt;

&lt;p&gt;In this guide, we delve deeper into notification APIs, their role in transactional and marketing notifications, practical use cases, and the indirect benefits they offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding and using notification APIs
&lt;/h2&gt;

&lt;p&gt;Here are some examples of Notification API features. These are capabilities that add notification capabilities to communication APIs such as Twilio, Microsoft Teams, MessageBird, Slack, and so on. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-channel send:&lt;/strong&gt; Notification APIs allow for dispatching notifications across &lt;a href="https://www.courier.com/docs/guides/tutorials/how-to-configure-multi-channel-routing/"&gt;multiple channels&lt;/a&gt;, like email, SMS, and push notifications. This frees developers from the complexities of maintaining individual infrastructures for each channel, streamlining the process considerably. For instance, an HR application might dispatch a job offer requiring a signature via SMS, a channel offering urgency and external reach, while using a tool like Slack—an internal, less urgent channel—for onboarding tasks with a new employee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delivery status awareness across channels:&lt;/strong&gt; A unique feature of notification APIs is their ability to track delivery status across all channels. This means, if an email, SMS, or push notification has been successfully delivered, it's accurately logged and reported. This insight enables developers to understand the efficacy of their communication across different platforms.&lt;/p&gt;

&lt;p&gt;Additionally, some sophisticated notification APIs also monitor “read statuses.” This means that when a user reads a notification on one platform, it can potentially halt the delivery or alter the display of notifications on other channels, for example marking them as opened or read. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automations:&lt;/strong&gt; From fetching the latest data for inclusion in a notification to setting up advanced delivery timings and rules across channels, notification APIs automate much of the orchestration process. Automations within notification APIs reduce manual effort and potential errors. This might involve sending a time-delayed, multi-channel sequence of notifications that stop as soon as the user reads them. Alternatively, it could involve digesting and batching frequent notifications based on preset or user-defined frequency or even throttling messages that trigger too often.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template management:&lt;/strong&gt; Many notification APIs offer tools for developers to efficiently design, manage, and repurpose notification templates across various channels. These include pre-made templates as well as the option to create your own, eliminating the necessity of crafting notifications from scratch every time. This allows developers to focus more on the message and its intended impact, enhancing efficiency and overall user engagement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Central logs and reporting:&lt;/strong&gt; Robust logging and reporting capabilities are integral to notification APIs. This is especially relevant for notifications because it can present delivery status across channels in a single view. These features provide invaluable insights and aid in troubleshooting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User preferences:&lt;/strong&gt; Notification APIs provide a simple way to collect and respect user preferences for notification topics, channels, and frequencies, ensuring notifications don’t overwhelm users. Eg. a user might want to receive billing notifications via both SMS and email. But they may want to receive system error notifications via PagerDuty. And they may want to hold all comments on a project, to be rolled up into a single email digest every week. All of this is possible with a Notification API, which provides both the frontend components for the UI as well as the backend logic to respect these preferences.  If all notifications get sent through a notification API, the service becomes a useful tool for controlling users’ notification preferences. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web and mobile notification center (Inbox):&lt;/strong&gt; In addition to external channels like email, Slack, and SMS, it often makes sense to hava a notification center, or "inbox" that lives inside of the web or mobile app. This typically also includes a bell icon with a change badge that counts number of messages, as well as related push notifications.  A notification API will not only handle all the back-end logic for this functionality, but it will typically also offer a complete set of web and mobile SDKs for building the frontend experience. This allows developers to construct a fully functional in-app inbox and preference center with a notification API, elevating the overall user experience with minimal additional development time. Here's an example of &lt;a href="https://www.courier.com/inbox/"&gt;Courier's inbox offering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internationalization:&lt;/strong&gt; Notification APIs offer the advantage of internationalization. If the API manages message templates, it’s possible to select a template appropriate for each user’s region and language or even work in sync with a translation API to serve translated versions of notification content. This ensures that each user receives personalized and localized notifications, further improving user engagement and satisfaction.&lt;/p&gt;

&lt;p&gt;By leveraging a notification API, a developer can save time on building these features without sacrificing the end users’ experience or creating technical debt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The role of notification APIs in transactional versus marketing notifications
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/5YVtyABAeqWfiJx4kOhrbL/cdf9608d0a077fd184b342ca0a2cc5a6/Notification_API_2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/5YVtyABAeqWfiJx4kOhrbL/cdf9608d0a077fd184b342ca0a2cc5a6/Notification_API_2.png" alt="A diagram summarizing transactional and marketing notification workflows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notification APIs are typically used for transactional notifications. However, the transition away from moment-in-time marketing campaigns to ongoing Growth loops, is making Notification APIs more appropriate for marketing notifications than traditional marketing automation tools. To understand why that’s the case, let’s quickly define these two notification categories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transactional notifications&lt;/strong&gt; are often triggered by user actions, such as making a purchase, resetting a password, or receiving a direct message. Or they may be tied to the functionality of a product feature - like a commenting feature in the app where you want to notify a person of an @mention.  As their name suggests, these notifications are tied to specific transactions or interactions a user has with the application, playing a continuous role in user experience. They might rely on external channels like email and SMS or, for SaaS apps, they may be used with internal channels such as Slack, MS Teams, and PagerDuty.&lt;/p&gt;

&lt;p&gt;Notification APIs prove particularly useful for transactional notifications due to their dynamic and event-triggered nature. By leveraging APIs, these real-time, personalized messages can be handled as they are generated, ensuring relevance.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;strong&gt;marketing notifications&lt;/strong&gt; are traditionally associated with promotional time-bound campaigns for a product, offer, or service. These messages focus on driving conversions or sales. Unlike transactional notifications, marketing notifications usually aren’t specific to the user or their activity in the app but instead get sent to larger groups of users. Marketing notifications also focus on external channels like email, SMS, push, and in-app.&lt;/p&gt;

&lt;p&gt;Marketing notifications have traditionally been managed through a marketing platform due to their mass distribution nature and the connection to marketing campaigns. Specialized marketing platforms typically address the need for features such as opt-in logic to comply with anti-spam regulations, campaign management, and detailed analytics of the conversion process.&lt;/p&gt;

&lt;p&gt;However, product growth experiments now cross the divide of marketing and are often more appropriate for Notification APIs than marketing automation tools. For example, if a user hits a key usage lifecycle milestone in your application, you may want to trigger a notification with a unique offer or with best practices to ensure their success. These kinds of notifications can drive higher application engagement and overall monetization of the app.&lt;/p&gt;

&lt;p&gt;Over time, notification APIs have the potential to bridge the gap between transactional and marketing notifications. This potential stems from notification APIs’ ability to offer more personalized, event-driven marketing notifications that adhere to the user’s behavior and preferences.&lt;/p&gt;

&lt;p&gt;In essence, notification APIs offer a robust framework that supports both transactional and marketing notifications, creating a cohesive and engaging user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of using a notification API for developers
&lt;/h2&gt;

&lt;p&gt;Notification APIs offer numerous indirect benefits that enhance efficiency, increase flexibility, and improve the developer experience.&lt;/p&gt;

&lt;p&gt;One key advantage of notification APIs is provider independence. APIs abstract the underlying implementation details, allowing developers to switch providers (eg. from Postmark to SenGrid) swiftly if necessary without disrupting the overall application function. They can even provide failover between channels and providers without having to build out complex logic.&lt;/p&gt;

&lt;p&gt;Notification APIs also offer consistency in notification formatting and delivery across various channels. Consistency improves user experience by ensuring each message is uniform, regardless of the channel through which it is sent.&lt;/p&gt;

&lt;p&gt;From a developer’s perspective, APIs simplify the integration and management of notification systems. Developers don’t need to write boilerplate code for every new channel or message type or worry if their abstractions will carry over into a new provider’s API schema. Instead, they leverage the API’s functionality and use whichever providers they need, which can save considerable time and effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample use cases for notification APIs
&lt;/h2&gt;

&lt;p&gt;Here are some examples of what you could build in your product using a notification API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status updates:&lt;/strong&gt; Inform users about the progression of their initiated actions, such as “Your order is out for delivery” in a food delivery app context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Appointment reminders:&lt;/strong&gt; Remind users about upcoming appointments, deadlines, or important dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval requests:&lt;/strong&gt; Request user approval for specific actions, such as document sharing or data access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication management:&lt;/strong&gt; Handle password resets or two-factor authentication prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation requests:&lt;/strong&gt; Ask users to confirm potentially irreversible actions or decisions, such as deleting data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing notifications:&lt;/strong&gt; Inform users about upcoming payments, send payment confirmations, or notify them of billing issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated invites:&lt;/strong&gt; Send automated invitations to events or cooperative endeavors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digests:&lt;/strong&gt; Provide digests or summaries on user-selected topics at a frequency and channel set by the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-commerce:&lt;/strong&gt; Handle cart abandonment notifications, shipping updates, or requests for credit card updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product engagement:&lt;/strong&gt; Prompt users to return and complete a task, enhancing user engagement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Survey requests:&lt;/strong&gt; Solicit user feedback through surveys or review requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Welcome and user onboarding messages:&lt;/strong&gt; Help new users get started with welcome messages and onboarding instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notify about a user’s activity:&lt;/strong&gt; Alert users about new logins or changes to a shared project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform engagement:&lt;/strong&gt; Encourage users to engage with a platform or service more frequently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Critical alerts:&lt;/strong&gt; Notify users about critical issues, such as security alerts, product downtime, or other urgent matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In each of these use cases, notification APIs streamline the process of sending timely, relevant, and personalized notifications to users, enhancing user engagement and satisfaction.&lt;/p&gt;

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

&lt;p&gt;Notification APIs have emerged as tools that enable developers to build product notification experiences that users love. By offering a streamlined way to manage notification content, delivery logic, internationalization, compliance, and user preferences, notification APIs reduce the complexity of building and maintaining the underlying infrastructure.&lt;/p&gt;

&lt;p&gt;Notification APIs also help improve the user experience. By running all notifications through a single service, it’s possible to offer users more flexible preferences and cross-channel syncing of notification statuses. Prior to Notification APIs, only the largest, most sophisticated engineering teams could build this level of infrastructure in-house.&lt;/p&gt;

&lt;p&gt;If you are interested in using a notification API, &lt;a href="https://www.courier.com/"&gt;Courier&lt;/a&gt; is considered the leader in the market, funded by Twilio, Slack, and Google and used by companies such as Comcast, LaunchDarkly, Contentful, and Lattice. Courier is free to use for up to 10,000 notifications per month.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>api</category>
      <category>notifications</category>
    </item>
    <item>
      <title>Using Prisma to Address Connection Pooling Issues in Serverless Environments</title>
      <dc:creator>Oleksii Klochai</dc:creator>
      <pubDate>Mon, 06 Dec 2021 14:57:32 +0000</pubDate>
      <link>https://forem.com/prisma/using-prisma-to-address-connection-pooling-issues-in-serverless-environments-3g66</link>
      <guid>https://forem.com/prisma/using-prisma-to-address-connection-pooling-issues-in-serverless-environments-3g66</guid>
      <description>&lt;p&gt;This article discusses what “serverless” environments are, why developers choose to deploy their apps to them, and how the &lt;a href="https://cloud.prisma.io/" rel="noopener noreferrer"&gt;Prisma Data Platform&lt;/a&gt; solves the problems that can arise regarding database connection pooling.&lt;/p&gt;

&lt;p&gt;Choosing which tools you will use to build your application, and which platform you will be deploying to in production, are early and vital decisions that you will have to make when building a product. &lt;/p&gt;

&lt;p&gt;Using an ORM like &lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; speeds up the development process, while serverless hosting reduces the ongoing cost and resource overhead of ensuring your application’s availability to your customers.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; is a next-generation ORM (&lt;a href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping" rel="noopener noreferrer"&gt;Object-Relational Mapping&lt;/a&gt;) library for Node.js and TypeScript.&lt;/p&gt;

&lt;p&gt;Prisma allows you to build your app in JavaScript or TypeScript and work with your data as objects rather than SQL or NoSQL queries. These objects are mapped to a database using a schema that generates all the types and conversions required to accurately store your objects in your database platform.&lt;/p&gt;

&lt;p&gt;The Prisma toolset includes developer tools for generating database migrations so that your database is always in-sync with your data mode as defined in the &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-schema" rel="noopener noreferrer"&gt;Prisma schema&lt;/a&gt;. In addition, the cloud-based &lt;a href="https://cloud.prisma.io/" rel="noopener noreferrer"&gt;Prisma Data Platform&lt;/a&gt; provides tools like user/role management and a data browser that allow you and your team to collaboratively manage your data online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Prisma in your projects means spending less time worrying about your database, and more time to focus on building your app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In addition to being a great aid to development, the &lt;a href="https://cloud.prisma.io/" rel="noopener noreferrer"&gt;Prisma Data Platform&lt;/a&gt; discussed in this article makes Prisma a flexible and efficient way to manage database connections for your application with minimal fuss.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is “serverless” and why do developers “go serverless”?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Serverless_computing" rel="noopener noreferrer"&gt;Serverless hosting&lt;/a&gt; is a means of delivering your app to your user without having to build and maintain a server to deploy it to.&lt;/p&gt;

&lt;p&gt;It may sound strange, but it is a cost effective way to deploy applications. Serverless cloud hosting providers like &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; and &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; take care of all of the infrastructure required to run your application.&lt;/p&gt;

&lt;p&gt;Here are some of the top reasons why developers are going serverless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced infrastructure requirements
&lt;/h3&gt;

&lt;p&gt;The hosting provider administers all of the infrastructure required to host your app—maintaining it, keeping it up-to-date, and most importantly, keeping it secure. The provider deals with the day-to-day burdens of hosting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streamlined deployment
&lt;/h3&gt;

&lt;p&gt;With serverless hosting you are provided tools to readily upload, run, and monitor your code. When a request comes in, your code is launched in a container and executed. The serverless environment discards the container when it is no longer needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced costs
&lt;/h3&gt;

&lt;p&gt;In addition to the time and resources saved by not having to maintain your own server infrastructure, you also save on the cost of running a full server 24/7. Many serverless hosting providers like AWS and Azure will only bill you for the resources you use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Serverless hosting offerings like &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; and &lt;a href="https://azure.microsoft.com/en-us/services/functions/?cdn=disable" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt; can support sustained high load thanks to the cloud providers’ globally distributed infrastructure. In addition, such systems are well-suited for handling bursts of high load amid periods of lower use due to their automatic scaling functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The issues with “connection pooling” in serverless environments
&lt;/h2&gt;

&lt;p&gt;Any database, serverless or not, has a limit on the number of open connections that it can handle at one time. For each open connection, the database system like &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; or &lt;a href="http://www.mysql.org/" rel="noopener noreferrer"&gt;MySQL&lt;/a&gt; reserves network, memory, and compute resources so that it’s able to handle any incoming requests through each connection.&lt;/p&gt;

&lt;p&gt;In a serverless environment your code does not run constantly. A user makes a request (whether it’s viewing a webpage or requesting an API resource), and a tiny containerized executing environment (called an instance) running only the code required to serve the request is launched.  The request is served, and the instance is destroyed.&lt;/p&gt;

&lt;p&gt;This is efficient, but there is one problem. In traditional hosting environments with a single server that runs your application,  a single instance of the application can service multiple user requests.&lt;/p&gt;

&lt;p&gt;In a traditional hosting environment a single instance of the application will be managing a &lt;strong&gt;single&lt;/strong&gt; &lt;a href="https://en.wikipedia.org/wiki/Connection_pool" rel="noopener noreferrer"&gt;connection pool&lt;/a&gt; to your database. The connection pool ensures that any database queries are within the connection and timeout limits that you have configured. It coordinates the re-use of database connections and prevents the database server from being overloaded.&lt;/p&gt;

&lt;p&gt;In a serverless environment, a new instance of your code is launched for every user request. Each instance will be unaware of the others, resulting in &lt;strong&gt;multiple&lt;/strong&gt; connection pools. As these separate connection pools are not aware of one another, they cannot coordinate to ensure the database server is not overloaded.&lt;/p&gt;

&lt;p&gt;In a serverless environment, there is no shared connection pool. This creates the risk that a small number of user requests exhaust the database connection limit causing the application to stop working.&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%2Fcu90f410g7ydf51tym17.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%2Fcu90f410g7ydf51tym17.png" alt="Each Serverless function handles one user request which can quickly exceed the database connection limit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Methods of dealing with with connection pooling issues
&lt;/h2&gt;

&lt;p&gt;There are three key ways in which the modern developer can deal with connection pooling issues in a serverless environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  External connection poolers
&lt;/h3&gt;

&lt;p&gt;One method is to use an &lt;a href="https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#external-connection-poolers" rel="noopener noreferrer"&gt;external connection pooler&lt;/a&gt;. By moving the task of coordinating database connections outside of your serverless app, an external connection pooler solves the issue of the separate execution instances not being able to coordinate with each other.&lt;br&gt;
Not all database servers have an external connection pooler. If this path you choose to mitigate the issue, you should carefully select which tools and platforms will be used in your project early in the development process.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://cloud.prisma.io/" rel="noopener noreferrer"&gt;Prisma Data Proxy&lt;/a&gt; is an example of an external connection pooler approach.&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%2Frt38aa2ho502ld80uvu0.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%2Frt38aa2ho502ld80uvu0.png" alt="Serverless with the Prisma Data Proxy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Adjusting your code
&lt;/h3&gt;

&lt;p&gt;Another method to overcome connection pooling issues is to ensure that your code is &lt;a href="https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#prismaclient-in-serverless-environments" rel="noopener noreferrer"&gt;written in a way&lt;/a&gt; that accounts for the serverless environment in which it will run. Some cloud providers &lt;em&gt;do&lt;/em&gt; reuse resources between requests, so structuring your code so that connections can be maintained between requests is a way to reduce database load. &lt;/p&gt;

&lt;p&gt;However, this approach is limited and will only help alleviate the problem to some degree. &lt;/p&gt;
&lt;h3&gt;
  
  
  Fine tuning your database server
&lt;/h3&gt;

&lt;p&gt;An important step in deploying to a production environment is to monitor and profile your application. Knowing how your application will be used and how busy it will be allows you to determine what resources to allocate. With this information, you can optimize your database connection to reduce the chance of overloading it. For example, you can reduce the connection timeout for inactive connections so that your database is less likely to be spending resources on a connection that’s not going to be used. Another option is to increase the connection limit on the database, but handling more connections will likely require more compute resources.&lt;/p&gt;

&lt;p&gt;In addition to supporting these traditional methods of dealing with connection pooling issues, the Prisma Data Platform introduces a reliable and readily configurable database proxy that solves connection pooling issues—regardless of the database server or database host in use.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solving connection pooling issues with Prisma Data Proxy
&lt;/h2&gt;

&lt;p&gt;Prisma has introduced a new service that effectively solves the connection pooling issue when deploying to serverless environments like AWS Lambda and Vercel.&lt;/p&gt;

&lt;p&gt;The Prisma Data Proxy acts as an intermediary between your application code and the database, effectively acting as an external connection pooler for any &lt;a href="https://www.prisma.io/docs/reference/database-reference/supported-databases" rel="noopener noreferrer"&gt;database supported by Prisma&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Flexible ORM and proxy with external connection pooling
&lt;/h3&gt;

&lt;p&gt;Using the Prisma Data Proxy solves the connection pooling issue without you having to drastically alter your code or spend extra time tweaking your database connection configuration to account for the serverless environment.&lt;/p&gt;

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

&lt;p&gt;The Prisma Data Proxy also reduces the size of the data bundle that you must upload to your serverless hosting environment. Many of the database functions are outsourced to the highly performant Prisma Engine binary hosted by the Prisma Data Proxy, so they do not need to be included with your deployment bundle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part of the Prisma Data Platform
&lt;/h3&gt;

&lt;p&gt;The Prisma Data Proxy is part of the &lt;a href="https://www.prisma.io/dataplatform" rel="noopener noreferrer"&gt;Prisma Data Platform&lt;/a&gt;, a cloud based platform that helps manage and collaborate on application data.&lt;/p&gt;

&lt;p&gt;The Prisma Data Platform includes a powerful databas tools to assist you and your team during the development process. This includes the Prisma Query Console, which enables you to view and manage your live data through a simple interface. It allows you to gain insight into how your application is used and to quickly fix any arising data issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benefits of serverless hosting and Prisma
&lt;/h2&gt;

&lt;p&gt;Serverless hosting in combination with the &lt;a href="https://cloud.prisma.io/" rel="noopener noreferrer"&gt;Prisma Data Platform&lt;/a&gt; provides a set of collaborative development and deployment tools for developers who simply want to get on with building their product.&lt;br&gt;
Building and running serverless applications has become a very attractive option for developers due to the reduced resources and costs, and an easier scaling approach.&lt;/p&gt;

&lt;p&gt;However, when developing your application you will have to ensure its suitability to be run in a serverless environment. There is nothing inherently wrong with the serverless concept, but there are differences to traditional hosting configurations that must be considered when coding to deploy in such an environment.&lt;/p&gt;

&lt;p&gt;The Prisma Data Platform is being actively developed to address the challenges of deploying data-intensive apps to modern serverless platforms while allowing you to use the database and database host you are already comfortable with.&lt;/p&gt;

&lt;p&gt;To learn more about databases in the Serverless era and a sneak peak into Prisma's vision for the Prisma Data Platform, check out the talks from the &lt;a href="https://www.prisma.io/serverless" rel="noopener noreferrer"&gt;Prisma Serverless Conference&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Get started with the Prisma Data Platform
&lt;/h3&gt;

&lt;p&gt;The Prisma Data Platform, including the Prisma Data Proxy, is currently available in Early Access.&lt;/p&gt;

&lt;p&gt;Online documentation is available to get you started, so that you are ready to implement the Prisma Data Platform in your project when it is released.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=iyGZ8JFPgoY" rel="noopener noreferrer"&gt;See the Prisma Data Proxy in action&lt;/a&gt; or &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-data-platform#prisma-data-proxy" rel="noopener noreferrer"&gt;learn more about how to easily integrate the Prisma Data Platform into your project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you are ready to try Prisma Data Platform out, &lt;a href="https://www.prisma.io/dataplatform" rel="noopener noreferrer"&gt;get started today&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>serverless</category>
      <category>database</category>
    </item>
  </channel>
</rss>
