As a software engineer and technical content creator, I work with a lot of companies on many different contracts. To get paid for my work, most companies require that I send an invoice. Sometimes they want one daily, at the end of the week, or even when the project has been completed.

Sending an invoice to my clients is crucial because it determines when and if I will get paid on time. If this sounds like a repetitive task that can eat deep into my productive hours, you are right. To keep my focus on getting my tasks done, I decided to set up an automated invoice workflow.

In this tutorial, I will show you how you can effectively generate and send an invoice to your client using the Invoice generator API and scheduled pipelines.


For this tutorial, you will need:

Cloning the demo application

For this tutorial, I have set up a simple Node.js project with an existing generateInvoice() function defined within the invoice.js file. To get started, run this command:

git clone automated-invoice

This clones the project into an automated-invoice folder within your development directory (or whenever you ran the command from).

Next, go to the newly created project and install its dependencies:

cd automated-invoice

npm install

Generating an invoice locally

To confirm that the project works as expected, run the application locally. Generate an invoice by running:

node invoice.js

The output should show that an invoice has been generated and saved within your project’s directory.

Saved invoice to invoice.pdf

Now you can set up sending the invoice as an attachment.

Sending the invoice as an attachment

Sending emails from any application deployed on the internet requires an SMTP server and a few other configurations. For this tutorial, I used Mailtrap, which offers free SMTP services for experimenting with email-sending functionality. If you have not yet done so, create a Mailtrap account here.

Next, go to the inbox page to view your SMTP and POP3 credentials.

Mailtrap dashboard

Creating an environment variable

Create a .env file at the root of the project using this command:

cp .env.sample .env

Replace the placeholders MAILTRAP_USERNAME and MAILTRAP_PASSWORD with the values on your dashboard.

Sending email with the invoice as an attachment

The application uses the nodemailer module to send email messages with the invoice as an attachment. The module is already installed. All you need to do is open the invoice.js file and replace its content with this:

var https = require("https");
var fs = require("fs");
var nodemailer = require("nodemailer");

const generateInvoice = (invoice, filename, success, error) => {
  var postData = JSON.stringify(invoice);
  var options = {
    hostname: "",
    port: 443,
    path: "/",
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": Buffer.byteLength(postData),
  var file = fs.createWriteStream(filename);
  var req = https.request(options, function (res) {
      .on("data", function (chunk) {
      .on("end", function () {
        if (typeof success === "function") {
  if (typeof error === "function") {
    req.on("error", error);

const sendEmail = (file) => {
  var transport = nodemailer.createTransport({
    host: "",
    port: 2525,
    auth: {
      user: process.env.MAILTRAP_USERNAME,
      pass: process.env.MAILTRAP_PASSWORD,
  var mailOptions = {
    from: "",
    to: "",
    subject: "Invoice for weekly payments via Node.js",
    text: "Find attached the weekly invoice from me. Thanks",
    attachments: [
        path: file,
  transport.sendMail(mailOptions, function (error, info) {
    if (error) {
    } else {
      console.log("Email sent: " + info.response);

let invoice = {
  logo: "",
  from: "Invoiced\n701 Brazos St\nAustin, TX 78748",
  to: "Awesome Company / Client",
  currency: "usd",
  number: "INV-0001",
  payment_terms: "Due for payment",
  items: [
      name: "Weekly technical content",
      quantity: 1,
      unit_cost: 500,
      name: "Employee Portal Management",
      quantity: 1,
      unit_cost: 1000,
  fields: {
    tax: "%",
  tax: 5,
  notes: "Thanks for being an awesome customer!",
  terms: "Looking forward to the payments",

  () => console.log("Saved invoice to invoice.pdf"),
  (err) => console.log(err)

The change to this code block modifies the file by requiring the nodemailer module. A sendEmail() function is created, which takes the generated file as an argument and sends it as an attachment. The attachment is sent to the recipient defined with the mailOptions variable. The *generateInvoice()* function calls the sendEmail() method and passes the appropriate argument to it.

Now, run the application again using:

node invoice.js

The output is:

Saved invoice to invoice.pdf
Email sent: 250 2.0.0 Ok: queued

This output shows that the invoice was generated and sent successfully. Go to your Mailtrap inbox to view the message.

Mailtrap inbox

Click Attachments to open the invoice.

Generate invoice

Adding the CircleCI configuration file

Next, you need to add the pipeline configuration for CircleCI to automate creating and sending invoices.

At the root of your project, create a folder named .circleci. In that folder, create a file named config.yml. In the newly created file, add this configuration:

version: 2.1
    working_directory: ~/project
      - image: cimg/node:17.4.0
      - checkout
      - run:
          name: Update NPM
          command: "sudo npm install -g npm"
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: Install Dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
            - ./node_modules
      - run:
          name: Run the application
          command: node invoice.js
          background: true

This script pulls in a Node.js Docker image and installs the project’s dependencies. The last step runs the command to generate and send out the invoice.

Next, set up a repository on GitHub and link the project to CircleCI. Review Pushing a project to GitHub for step-by-step instructions.

Log in to your CircleCI account. If you signed up with your GitHub account, all your repositories will be available on your project’s dashboard.

Click Set Up Project for your automated-invoice project

Setup project on CircleCI

You will be prompted with a couple of options regarding the configuration file. Select the use the .circleci/config.yml in my repo option. Enter the name of the branch where your code is housed on GitHub, then click the Set Up Project button.

Select configuration

Your first workflow will start running, but it will fail. This is because you have not provided configuration details for Mailtrap. You can fix that now.

Click the Project Settings button, then click Environment Variables. Add these two new variables:


Rerun the workflow. The invoice will be generated and the email will be sent.

Successful Pipeline

Setting up a scheduled pipeline

Often, a CircleCI workflow is executed automatically once you push your code to a repository. For this tutorial, though, the goal is to run this pipeline at a specific interval, preferably every week. Using scheduled pipelines on CircleCI, you can configure your pipeline like a cron job and run it at an interval. There are two ways to do this:

  • Using API
  • Using project settings

For this tutorial, we will configure pipeline triggers using the project settings. From your project, go to Project Settings. Click Triggers from the menu on the left sidebar.

On the Triggers page, click Add Scheduled Trigger. Fill in the trigger form to configure the pipeline.

Schedule Trigger

Here is what to enter for these fields:

  • Trigger name is the unique schedule name.
  • Trigger Source indicates the source that will trigger the pipeline (for this tutorial it is scheduled).
  • Trigger description is an optional field you can use to add more information about the trigger.
  • Timetable defines when and how often to run the scheduled pipelines.
  • Pipeline Parameters are variables declared within the parameter key. They can be used to check when to run the pipeline, but you do not need them for this tutorial.
  • Attribution specifies the user associated with the schedule. It can be the system for a neutral actor. Or it can be current, which takes your current user’s permissions (as per the token you are using).

These settings configure the pipeline to run every five minutes, every day. Click Save Trigger to activate it.

You can view the created trigger from the Project Triggers page.

Project Triggers page

Return to the Pipelines page and wait for at least five minutes. Your pipeline will be triggered by the CircleCI system.

Pipeline schedule results

You can also check your Mailtrap inbox to review the generated invoices.

Mailtrap multiple inbox list


In this tutorial, you were able to generate an invoice on the fly and send it to a specific recipient without much hassle. To automate the invoicing process, we used the scheduled pipeline features from CircleCI.

Because what you implemented here is minimal, it can be easily included in an existing project or a new one. Another advantage of this workflow is that you can maintain continuous integration and deployments for your project while CircleCI handles invoicing automatically. This addition removes a tedious manual task and automates it so that you can spend more time developing and deploying applications.

I hope that you found this tutorial helpful. The complete source code can be found here on GitHub.

Oluyemi is a tech enthusiast with a background in Telecommunication Engineering. With a keen interest in solving day-to-day problems encountered by users, he ventured into programming and has since directed his problem solving skills at building software for both web and mobile. A full stack software engineer with a passion for sharing knowledge, Oluyemi has published a good number of technical articles and blog posts on several blogs around the world. Being tech savvy, his hobbies include trying out new programming languages and frameworks.

Read more posts by Olususi Oluyemi