How to Automate Personalized Videos in Kit (formerly ConvertKit) Newsletters with Clipcat API (Node.js Guide)

Set up an automated workflow to generate and send personalized videos in your Kit (formerly ConvertKit) newsletters. This step-by-step guide is for developers who want to boost email engagement with personalized video content automatically.
by Josephine Loo ·

Contents

    Email marketing is far from dead, but generic newsletters are. In a world flooded with content, personalization is what makes your emails stand out. Emails with personalized content have a 26% higher open rate, and when you combine that with video, click-through rates (CTR) can improve by up to 300%!

    That’s why combining personalized videos with your email marketing campaigns can be such a powerful strategy. In this tutorial, you’ll learn how to set up an automation workflow that helps you generate personalized videos and include them in your Kit (formerly ConvertKit) emails.

    You can use it to welcome new users, thank your top customers, or even hype up a product launch. This guide will walk you through everything in a way that’s developer-friendly and easy to follow. Let’s get started!

    What You’ll Be Building

    You'll build a complete workflow that automatically generates personalized videos for your subscribers and includes them in a Kit newsletter.

    Here’s how the flow looks like:

    1. A subscriber gets tagged in Kit (e.g., using a tag like generate_video).

    2. Kit triggers a webhook to your server (we'll expose it using ngrok for local testing).

    3. Your server receives the webhook and calls the Clipcat API to render a video personalized with the subscriber’s name.

    4. Once the video is ready, your server calls Kit's API to update the subscriber’s custom fields (video_url and thumbnail_url).

    5. You can then use these custom fields in your Kit email using Liquid to display a thumbnail image and link to the personalized video.

    a GIF showing how the email that a subscriber will receive will look like.gif

    This setup gives you full control over when videos are generated, and you can easily scale your email personalization without having to manually edit anything.

    Prerequisites

    Part 1. Set Up Your Clipcat Video Template

    Sign in to Clipcat (or sign up if you haven’t) and create a new project in your account. Then, duplicate the template below to your project by clicking "Use This Template" :

    A Clipcat video template can include multiple graphic or video scenes, each one made up of different objects like text or shapes (rectangles or circles). You can update these scenes and objects directly through Clipcat’s API, and this lets you generate different videos dynamically without touching the editor.

    The template we're using in this tutorial has five scenes, with text objects like message and name, as well as shape objects like background and opacity:

    a screenshot of Clipcat template - Personalized Real Estate Newsletter Template.png

    🐱 Meow Memo: Read this guide to learn more about how to customize a video template in Clipcat.

    Part 2. Configure Kit

    Next, we’ll set up Kit to trigger a webhook to your server when a specific event happens. For this tutorial, we’ll use the tag_add event as our trigger. So whenever a certain tag is added to a subscriber, it will trigger the personalized video automation in Clipcat.

    Using tags as a trigger is super flexible because:

    • You can manually add the tag from your Kit dashboard
    • Tags can be automatically applied through automation rules (e.g., after a form submission or email click)
    • You can bulk-tag existing subscribers to generate videos for many people at once

    This gives you full control over when and for whom videos get created, without relying solely on subscriber actions.

    🐱 Meow Memo: While we’ll use tag_add as our example in this tutorial, Kit’s webhook system also supports other trigger events such as subscriber_activate, subscriber_unsubscribe, subscriber_bounce, and more.

    Step 1. Get the API Key

    Head to your Kit account settings and navigate to the Developer section (click your profile icon > Settings > Developer ). Then, click “Add a new key” to generate a new API key:

    getting your Kit:Kit API key.png

    Copy the API key and save it somewhere safe. You’ll need it later to interact with the API.

    copying your Kit:Kit API key.png

    Step 2. Create a New Tag

    Let’s create a new tag so that every time it’s added to a subscriber, it will trigger the webhook to generate a personalized video. Go to Grow > Subscribers , then click on “Create a Tag” :

    creating a new tag for the subscribers.png

    Enter a name for your tag (e.g., generate_video):

    entering the name for the new tag.png

    Once the tag is created, click on it to open the tag page. You’ll find the tag ID in the URL, after _id=:

    getting the tag ID from the URL.png

    Step 3. Create a Webhook

    We can create a webhook directly using Kit’s API Playground. Just head over to the Webhooks section in their API reference and click on “Try it” to open the playground:

    trying the webhook API from the Kit API Playground.png

    In the API Playground, fill in the fields as below:

    • X-Kit-Api-Key: Your Kit API key
    • target_url: The URL that will receive the webhook
    • event.name: Type subscriber.tag_add
    • event.tag_id: The tag ID you created earlier

    sending a POST reqeust to the webhook API from the Kit API Playground.png

    To expose your localhost to receive the webhook, just run this command in your terminal/command prompt:

    ngrok http 3000
    

    🐱 Meow Memo: If your localhost isn't running on port 3000, just update it to match the port you're using.

    You’ll get a public URL like:

    https://abc123.ngrok-free.app
    

    So, if your endpoint for receiving the webhook is /webhook/generate-video, your target_url would be:

    https://abc123.ngrok-free.app/webhook/generate-video
    

    Step 4. Create Custom Fields

    To store the video and thumbnail URLs for each subscriber, you’ll need to create two custom fields. From your Kit dashboard, go to Grow > Subscribers , click on any subscriber, and add two fields named thumbnail_url and video_url. This way, we can update these fields to store the URLs later using the API.

    adding custom fields to Kit:Kit subscriber.png

    Part 3. Create an Express Server to Handle the Webhook

    Now, let’s build an Express server that will receive webhook data from Kit, call the Clipcat API to generate a personalized video, and update the subscriber’s custom fields with the video and thumbnail URLs.

    The logic is separated into three files to keep things modular and maintainable:

    • index.js: The main server and webhook handler
    • clipcat.js: Handles video generation via the  Clipcat API
    • kit.js: Handles subscriber updates and tag removal in Kit

    Step 1. Create the Project Directory

    In your terminal/command prompt, run the command below to create a new project directory and navigate to the folder:

    mkdir newsletter-video-automation
    cd newsletter-video-automation
    

    In the folder, create files following the file structure below:

    ├── index.js # Entry point of your server
    ├── services
    │ ├── clipcat.js # Handles video generation with Clipcat API
    │ └── kit.js # Handles Kit subscriber updates
    ├── .env # Stores your API keys and config
    

    Step 2. Initialize the Project

    In the same folder, run the command below to set up a new Node.js project and install all the dependencies you'll need:

    npm init -y
    npm install express body-parser dotenv
    

    After running the commands above, your folder should look like this:

    How to Automatically Create Personalized Videos for Kit:Kit Newsletters Using Clipcat API - Node.js project file structure.png

    Step 3. Set Up Your ‘.env’ File

    In your .env file, store all the sensitive keys and config settings:

    PORT=3000
    
    # Clipcat API
    CLIPCAT_API_KEY=your_clipcat_api_key
    CLIPCAT_TEMPLATE_ID=your_clipcat_template_id
    
    # Kit API
    KIT_API_KEY=your_Kit_api_key
    KIT_TAG_ID=your_generate_video_tag_id
    

    🐱 Meow Memo: By using environment variables, you avoid hardcoding sensitive credentials in your codebase. It’s safer and easier to manage.

    Step 4. Complete ‘index.js’

    This file sets up your main Express server, listens for webhook events from Kit, and handles the whole process: receive subscriber info → generate video → update subscriber with the video and thumbnail URLs → remove the trigger tag.

    const express = require('express');
    const bodyParser = require('body-parser');
    require('dotenv').config();
    
    const { generateVideo } = require('./services/clipcat');
    const { updateSubscriber, removeTag } = require('./services/kit');
    
    const app = express();
    app.use(bodyParser.json());
    
    app.post('/webhook/generate-video', async (req, res) => {
      res.status(200).json({ message: 'Accepted' }); // place here to avoid re-triggering the webhook
      const subscriber = req.body.subscriber;
      const subscriberName = subscriber.first_name;
      const subscriberId = subscriber.id;
      const subscriberEmail = subscriber.email_address;
    
      console.log(`Received webhook for subscriber: ${subscriberName}, ${subscriberId}, ${subscriberEmail}`);
    
      try {
        // Generate video via Clipcat
        const { videoUrl, thumbnailUrl } = await generateVideo(subscriberName);
        console.log('Video URL: ' + videoUrl);
        console.log('Thumbnail URL: ' + thumbnailUrl);
    
        // Update subscriber in Kit
        await updateSubscriber(subscriberId, subscriberEmail, videoUrl, thumbnailUrl);
        await removeTag(subscriberId, process.env.KIT_TAG_ID);
      } catch (err) {
        console.error(err.message);
        res.status(500).json({ error: 'Internal error' });
      }
    });
    
    app.listen(process.env.PORT, () => console.log(`Server running on port ${process.env.PORT}`));
    

    🐱 Meow Memo:  The res.status(200) response is returned immediately to prevent Kit from retrying the webhook while video rendering is still in progress.

    Step 5. Complete ‘clipcat.js’

    The generateVideo() function in this file calls the Clipcat API to generate a video using the template we set up earlier, but changing the name object to the subscriber’s name. After that, we’ll keep polling Clipcat's /renders/${videoId} endpoint to check when the result. Once the video generation is done, the function will return both the video URL and the thumbnail (preview) URL:

    require('dotenv').config();
    
    async function generateVideo(name) {
      console.log('Generating video...');
      var data = {
        template: process.env.CLIPCAT_TEMPLATE_ID,
        modifications: [
          {
            scene: 'Greetings',
            object: 'name',
            text: name,
          },
        ],
      };
      const res = await fetch('https://api.clipcat.com/v1/renders', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${process.env.CLIPCAT_API_KEY}`,
        },
      });
      const resData = await res.json();
      const videoId = resData.uid;
    
      let videoUrl, thumbnailUrl;
    
      while (!videoUrl && !thumbnailUrl) {
        console.log(`Polling the result for videoID: ${videoId}...`);
        const pollingRes = await fetch(`https://api.clipcat.com/v1/renders/${videoId}`, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${process.env.CLIPCAT_API_KEY}`,
          },
        });
        const pollingData = await pollingRes.json();
        if (pollingData.status == 'completed') {
          videoUrl = pollingData.url;
          thumbnailUrl = pollingData.preview;
        }
    
        await new Promise((resolve) => setTimeout(resolve, 5000));
      }
    
      return { videoUrl, thumbnailUrl };
    }
    
    module.exports = { generateVideo };
    

    Step 6. Complete ‘kit.js’

    This file contains two functions:

    1. updateSubscriber() — updates the subscriber’s custom fields with the video and thumbnail URLs generated by Clipcat.
    2. removeTag() — removes the tag from the subscriber so you can reapply it again in the future.

      require('dotenv').config();

      async function updateSubscriber(id, email, videoUrl, thumbnailUrl) { var data = { email_address: email, fields: { video_url: videoUrl, thumbnail_url: thumbnailUrl, }, };

      await fetch(https://api.kit.com/v4/subscribers/${id}, { method: 'PUT', headers: { 'X-Kit-Api-Key': process.env.KIT_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); }

      async function removeTag(subscriberId, tagId) { console.log(Removing tag (${tagId}) from subscriber (${subscriberId}) ...);

      const options = { method: 'DELETE', headers: { 'X-Kit-Api-Key': process.env.KIT_API_KEY } };

      const res = await fetch(https://api.kit.com/v4/tags/${tagId}/subscribers/${subscriberId}, options); if (!res.ok) { const errorText = await res.text(); // fallback in case there's no JSON throw new Error(Failed to remove tag: ${res.status} - ${errorText}); } console.log("Tag removed successfully"); }

      module.exports = { updateSubscriber, removeTag };

    Step 7. Run the Server

    Start the server locally by running the command below in your terminal/command prompt:

    node index.js
    

    Once this is running, adding the generate_video tag to a subscriber in Kit will trigger the flow. It’ll generate a video using the subscriber’s name, then update their custom fields (video_url and thumbnail_url) with the result.

    🐱 Meow Memo:  Make sure that your locally-hosted server is exposed using ngrok and the public URL matches the one you set as the webhook’s target URL. Otherwise, the webhook won’t reach your server.

    Part 4. Use the Video and Thumbnail in Your Emails

    With video_url and custom_url saved to each subscriber’s profile, you can now reference them in email broadcasts using the Liquid template language in an HTML block:

    <a href="{{ subscriber.video_url }}" style="position: relative; display: inline-block;">
      <img src="{{ subscriber.thumbnail_url }}" alt="Play" style="width:600px;">
    </a>
    <p>Click <a href="{{ subscriber.video_url }}">here</a> to watch the video.</p>
    

    Here’s what the email will look like when the subscriber receives it:

    a GIF showing how the email that a subscriber will receive will look like.gif

    🐱 Meow Memo:  View the full code on GitHub.

    Bonus Tip: Use a Play Button Thumbnail for Better Engagement

    You might be wondering, “Can I embed the video directly in the email?” Unfortunately, most email clients don’t support embedded videos, and overlaying a play button using HTML/CSS inside the email body isn’t reliably supported either.

    To work around this, you can use an image thumbnail with a play button already added to the image itself, then link it to the video URL. It looks like a playable video, even though it's technically just a clickable image.

    a screenshot of modification for the email broadcast using Bannerbear.png

    To do this, you can use an image generation API like Bannerbear to automatically create an image with the subscriber’s name on it. Once the image is generated, just save the result and use it in your Kit email, just like how you did with the video.

    🐱 Meow Memo: We’re not going into those steps in this tutorial since image manipulation isn’t the main focus. However, if you’re looking to enhance the outcome, it’s a great next step to explore!

    Conclusion

    Personalized content is one of the best ways to build stronger relationships with your subscribers, and with this setup, you can automate the process at scale. Once you’ve got everything running smoothly, you can reuse the same workflow for different use cases, such as:

    • After someone checks out
    • When a membership gets renewed
    • As part of a re-engagement campaign for inactive users

    Clipcat has a wide selection of templates in its library. Feel free to explore and pick one that matches your brand or campaign style. If you haven’t already, sign up for a Clipcat account and see how easy it is to bring personalised video content into your next campaign!

    About the authorJosephine Loo
    Josephine is an automation enthusiast. She loves automating stuff and helping people to increase productivity with automation.
    How to Automate Personalized Videos in Kit (formerly ConvertKit) Newsletters with Clipcat API (Node.js Guide)
    How to Automate Personalized Videos in Kit (formerly ConvertKit) Newsletters with Clipcat API (Node.js Guide)