quambene
pigeon-rs
Rust

Open source email automation written in Rust

Last updated May 16, 2026
82
Stars
5
Forks
0
Issues
0
Stars/day
Attention Score
9
Language breakdown
Rust 99.9%
HTML 0.1%
โ–ธ Files click to expand
README

Pigeon

latest version documentation build status codecov dependency status

Pigeon is a command line tool for automating your email workflow in a cheap and efficient way. Utilize your most efficient dev tools you are already familiar with.

For example, query the subscribers of your newsletter, create a plaintext and html email from a template file, and send it to all of them:

bash
pigeon send-bulk \
    sender@your-domain.com \
    --receiver-query "select email from user where newsletter_confirmed = true" \
    --message-file "message.yaml" \
    --display \
    --assume-yes
console
> Display query result: shape: (4, 1)
+------------------------------+
| email                        |
| ---                          |
| str                          |
+==============================+
| "marie@curie.com"            |
+------------------------------+
| "alexandre@grothendieck.com" |
+------------------------------+
| "emmy@noether.com"           |
+------------------------------+
| "elie@cartan.com"            |
+------------------------------+
> Sending email to 4 receivers ...
marie@curie.com ... ok
alexandre@grothendieck.com ... ok
emmy@noether.com ... ok
elie@cartan.com ... ok
- Install Pigeon from crates.io - Install Pigeon from github.com - Send email to a single receiver - Send bulk email to multiple receivers - Personalize your emails - How to connect to SMTP server - How to connect to email provider API - How to connect to postgres database - Email protocols - Third-party APIs - Data sources

Install Pigeon

Install Pigeon from crates.io

bash

Build and install pigeon binary to ~/.cargo/bin

cargo install pigeon-rs

Note: Run cargo install pigeon-rs again to update to the latest version. Uninstall the pigeon binary with cargo uninstall pigeon-rs.

Install Pigeon from github.com

bash

Clone repository

git clone git@github.com:quambene/pigeon-rs.git cd pigeon-rs

Build and install pigeon binary to ~/.cargo/bin

cargo install --path .

Note: Add $HOME/.cargo/bin to your PATH if it is missing:

bash
export PATH="$HOME/.cargo/bin:$PATH"

Getting help

For getting help, try one of the following:

bash

Check version

pigeon --version

Print help

pigeon --help

Print help for subcommand

pigeon help send pigeon help send-bulk pigeon help connect pigeon help init pigeon help query pigeon help simple-query pigeon help read

Usage

Check connection to your SMTP server with pigeon connect:

bash
pigeon connect
Connecting to SMTP server 'email-smtp.eu-west-1.amazonaws.com' ... ok

See currently supported integrations and how to connect below.

Note: You can also check connection to third-party APIs instead of using the SMTP protocol. For example, using AWS Simple Email Service (SES): pigeon connect aws.

Send email to a single receiver

Send a single email with subject and content:

bash
pigeon send \
    sender@your-domain.com \
    receiver@gmail.com \
    --subject "Test subject" \
    --content "This is a test email."

Send a single email with message defined in separate template file:

bash
pigeon send \
    sender@your-domain.com \
    receiver@gmail.com \
    --message-file "message.yaml"

The message template message.yaml is created with subcommand init:

bash
pigeon init

Note: One of the advantages of a --message-file is that you can also draft the html version of your email. In contrast, with the options --subject and --content the email will only be sent in plaintext format.

If you prefer a dedicated HTML file for drafting your email, use the following command:

bash
pigeon send \
    sender@your-domain.com \
    receiver@gmail.com \
    --subject "Test subject" \
    --text-file "./message.txt" \
    --html-file "./message.html"

where --text-file defines the plaintext and --html-file the HTML version of your email.

Send bulk email to multiple receivers

For example, query relevant users which confirmed to receive your newsletter, and send an email to all of them.

Let's check the query first via pigeon query:

bash
pigeon query --display "select email from user where newsletter_confirmed = true"
console
> Display query result: shape: (4, 1)
+------------------------------+
| email                        |
| ---                          |
| str                          |
+==============================+
| "marie@curie.com"            |
+------------------------------+
| "alexandre@grothendieck.com" |
+------------------------------+
| "emmy@noether.com"           |
+------------------------------+
| "elie@cartan.com"            |
+------------------------------+

See how to connect below to connect your database.

Note: You can also --save your query as a csv file: pigeon query --save <my-query>.

Now send your newsletter to the queried receivers. If the table column name is different to "email" use --receiver-column to define a different column name. Let's try a --dry-run without confirmation --assume-yes first:

bash
pigeon send-bulk \
    albert@einstein.com \
    --receiver-query "select email from user where newsletter_confirmed = true" \
    --message-file "message.yaml" \
    --assume-yes \
    --dry-run
console
> Sending email to 4 receivers ...
marie@curie.com ... dry run
alexandre@grothendieck.com ... dry run
emmy@noether.com ... dry run
elie@cartan.com ... dry run

After double checking, you can submit the same command without --dry-run. Remove --assume-yes as well for explicit confirmation.

Note: You can also send a bulk email to email adresses defined in a csv file instead of a query result. In this case, use option --receiver-file instead of --receiver-query. You can check the contents of a csv file via subcommand read, e.g. pigeon read recipients.csv.

Personalize your emails

If you need more individual emails, you can personalize your emails with option --personalize. Again, let's start by checking the relevant query:

bash
pigeon query --display "select firstname, lastname, email from user where newsletter_confirmed = true"
console
> Display query result: shape: (4, 3)
+-------------+----------------+------------------------------+
| firstname  | lastname      | email                        |
| ---         | ---            | ---                          |
| str         | str            | str                          |
+=============+================+==============================+
| "Marie"     | "Curie"        | "marie@curie.com"            |
+-------------+----------------+------------------------------+
| "Alexandre" | "Grothendieck" | "alexandre@grothendieck.com" |
+-------------+----------------+------------------------------+
| "Emmy"      | "Noether"      | "emmy@noether.com"           |
+-------------+----------------+------------------------------+
| "Elie"      | "Cartan"       | "elie@cartan.com"            |
+-------------+----------------+------------------------------+

In your message template message.yaml use variables in curly brackets, like {firstname} and {lastname}. Then define personalized colums as parameters for option --personalize. Finally, let's display everything with --display:

bash
pigeon send-bulk \
    albert@einstein.com \
    --receiver-query "select firstname, lastname, email from user where newsletter_confirmed = true" \
    --message-file "message.yaml" \
    --personalize "firstname" "lastname" \
    --display
console
> Display message file: MessageTemplate {
    message: Message {
        subject: "Issue No. 1",
        text: "Dear {firstname} {lastname},
            Welcome to my newsletter. We are doing hard sciences here.
            Sincerely, Albert Einstein",
        html: "Dear {firstname} {lastname},
            Welcome to my newsletter. We are doing hard sciences here.
            Sincerely, Albert Einstein",
    },
}
> Display emails: BulkEmail {
    emails: [
        Email {
            sender: "albert@einstein.com",
            receiver: "marie@curie.com",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Marie Curie,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Marie Curie,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                },
        },
        Email {
            sender: "albert@einstein.com",
            receiver: "alexandre@grothendieck.com",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Alexandre Grothendieck,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Alexandre Grothendieck,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
            },
        },
        Email {
            sender: "albert@einstein.com",
            receiver: "emmy@noether.com",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Emmy Noether,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Emmy Noether,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
            },
        },
        Email {
            sender: "albert@einstein.com",
            receiver: "elie@cartan.com",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Elie Cartan,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Elie Cartan,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
            },
        },
    ],
}
> Should an email be sent to 4 recipients? Yes (y) or no (n)
>

Confirm y if you are ready to go.

How to connect

How to connect to SMTP server

To connect to a SMTP server, define environment variables SMTPSERVER, SMTPUSERNAME, and SMTP_PASSWORD. For example, using AWS SES:

bash
SMTP_SERVER=email-smtp.eu-west-1.amazonaws.com
SMTP_USERNAME=...
SMTP_PASSWORD=...

Source your environment .env in your current shell:

bash
set -a && source .env && set +a

How to connect to email provider API

Instead of using SMTP, you can send emails via the API of a specific email provider as well.

Using AWS SES, define the following environment variables:

bash
AWSACCESSKEY_ID=...
AWSSECRETACCESS_KEY=...
AWS_REGION=eu-west-1

where AWS_REGION depends on the specified region for your AWS SES account.

Source your environment again:

bash
set -a && source .env && set +a

Send an email using --connection:

rust
pigeon send \
    sender@your-domain.com \
    receiver@gmail.com \
    --connection aws \
    --message-file "message.yaml"

How to connect to postgres database

For postgres, the database url is constructed as follows: postgresql://dbuser:dbpassword@dbhost:dbport/db_name.

Therefore, set the following environment variables in your environment .env:

  • DB_HOST
  • DB_PORT
  • DB_USER
  • DB_PASSWORD
  • DB_NAME
Source your environment again:
bash
set -a && source .env && set +a

CAUTION: Connecting via TLS is not supported yet. Forward a local port through a SSH tunnel instead, e.g.:

bash
pigeon query "select email from user where newsletter_confirmed = true" --display --ssh-tunnel 5437

In addition to the environment variables above, SERVERUSER and SERVERHOST have to be set for the SSH connection (ssh user@host).

Integrations

Email protocols

  • MIME
  • SMTP

Third-party APIs

  • AWS SES

Data sources

  • PostgreSQL
  • CSV

Comparison with Mailchimp, Sendgrid, and ConvertKit

These numbers may be outdated. Do your own research.

The following table compares the price per month for email provider and emails per month.

  | 5,000 | 10,000 | 100,000 --- | --- | --- | --- Pigeon+AWS | $4.50 | $5 | $14 Mailchimp Marketing | $9.99 | $20.99 | $78.99 Mailchimp Transactional | - | - | $80 Sendgrid Marketing | $15 | $15 | $120 Sendgrid API | $14.95 | $14.95 | $29.95 ConvertKit | $66 | $100 | $516

The following table shows the daily limit for sent emails per provider.

provider | daily limit --------- | --------- Pigeon+AWS | 50,000 Mailchimp | equals monthly limit Sendgrid | equals monthly limit

Testing

Some integration tests require a locally running postgres database:

  • Specify the following environment variables:
- DB_HOST - DB_PORT - DB_USER - DB_PASSWORD - DB_NAME
  • Set up a temporary postgres db: docker-compose run --rm --service-ports postgres
bash

Run unit tests and integration tests

cargo test

Run unit tests

cargo test --lib

Run integration tests

cargo test --test '*'
๐Ÿ”— More in this category

ยฉ 2026 GitRepoTrend ยท quambene/pigeon-rs ยท Updated daily from GitHub