CLI mail merge tool that creates Gmail drafts from a CSV and Markdown template
Project description
DraftMachine
CLI mail merge tool — creates Gmail drafts from a CSV and a Markdown template.
Every existing CLI mail merge tool sends immediately. DraftMachine creates drafts instead: review them in Gmail's UI, edit any outliers, then send when ready.
Install
pip install draftmachine
Or from source:
git clone https://github.com/YOUR_USERNAME/draftmachine
cd draftmachine
pip install .
Quick start
-
Set up your GCP credential (one-time):
draftmachine setup -
Create a template (
template.md):--- subject: Hey {{first_name}}, quick note about {{company}} --- Hi {{first_name}}, I noticed that **{{company}}** is doing great things lately. Best, John
-
Create your email list (
list.csv):email,first_name,company alice@example.com,Alice,Acme bob@example.com,Bob,Globex
-
Preview before creating (renders row 1, no API calls):
draftmachine send list.csv template.md --preview
-
Create the drafts:
draftmachine send list.csv template.md # ✓ Created 2 drafts. Open Gmail to review.
Template syntax
Templates are Markdown files with YAML frontmatter. Any CSV column header becomes a {{variable}} in the template.
Jinja2 is the template engine — conditionals, filters, and loops all work:
---
subject: {% if vip %}[VIP] {% endif %}Hi {{first_name}}
---
Hi {{first_name}},
{% if company %}
You're at **{{company}}** — let's talk.
{% else %}
I'd love to connect.
{% endif %}
Options
| Flag | Description | Default |
|---|---|---|
--preview |
Render row 1 to terminal, no API calls | off |
--to-column |
CSV column to use as recipient address | email |
# Use a different column as the email address
draftmachine send list.csv template.md --to-column=work_email
GCP credential setup (manual steps)
draftmachine setup walks you through this, but here are the steps:
- Go to Google Cloud Console
- Create a project (or select an existing one)
- Enable the Gmail API: APIs & Services → Enable APIs → search "Gmail API" → Enable
- Create an OAuth credential: APIs & Services → Credentials → + Create Credentials → OAuth client ID
- Application type: Desktop app
- Download the JSON file and move it to
~/.draftmachine/client_secret.json - Run
draftmachine setupto complete the OAuth consent flow
The tool uses the gmail.compose scope — draft creation only, no ability to send or read your email.
Note: For personal use (your own Gmail account), the "This app is not verified" warning is expected and safe to proceed through. For distribution to other users, Google's OAuth app verification is required.
Known limitations
-
--previewshows row 1 only. It's a quick sanity check, not a full dry-run. Pass 1 (the two-pass render) validates all rows before any drafts are created — so template errors on any row are caught before Gmail is touched. -
Quota errors leave partial state. If the Gmail API returns a 429 (quota exceeded) partway through draft creation, some drafts will have been created and others won't. DraftMachine reports how many succeeded. V1 has no resume mechanism — check your Gmail Drafts folder before retrying to avoid duplicates.
-
Always uses your Gmail account's default sender. If you have send-as aliases configured, the draft's From address will be your primary Gmail address.
--fromalias support is planned.
Development
git clone https://github.com/YOUR_USERNAME/draftmachine
cd draftmachine
pip install -e ".[dev]"
pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file draftmachine-0.1.0.tar.gz.
File metadata
- Download URL: draftmachine-0.1.0.tar.gz
- Upload date:
- Size: 12.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55467002737d0127d8a25aae85116c298f4d4120c49afc87493f6f305311decf
|
|
| MD5 |
1798e97cca4d83c427d3fe19d3ca476b
|
|
| BLAKE2b-256 |
76dd17259528c60de47f885dae4ab6ee659bc4c525194e5def9f32635f0126ad
|
Provenance
The following attestation bundles were made for draftmachine-0.1.0.tar.gz:
Publisher:
publish.yml on audiojak/draftmachine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
draftmachine-0.1.0.tar.gz -
Subject digest:
55467002737d0127d8a25aae85116c298f4d4120c49afc87493f6f305311decf - Sigstore transparency entry: 1176985836
- Sigstore integration time:
-
Permalink:
audiojak/draftmachine@1e42da4254de6de5889ed7945f0a48e13c3cad75 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/audiojak
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1e42da4254de6de5889ed7945f0a48e13c3cad75 -
Trigger Event:
push
-
Statement type:
File details
Details for the file draftmachine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: draftmachine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
611d2ea142ac9160c0a9e6f61d23f9bebd998c5e6d61ec099bb148fac2b817fc
|
|
| MD5 |
ad8875035885cc02077728c068fb0fdb
|
|
| BLAKE2b-256 |
a25d0da175b18573416a4494aaf885b6881554142868a5e95b805243c001cc23
|
Provenance
The following attestation bundles were made for draftmachine-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on audiojak/draftmachine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
draftmachine-0.1.0-py3-none-any.whl -
Subject digest:
611d2ea142ac9160c0a9e6f61d23f9bebd998c5e6d61ec099bb148fac2b817fc - Sigstore transparency entry: 1176986285
- Sigstore integration time:
-
Permalink:
audiojak/draftmachine@1e42da4254de6de5889ed7945f0a48e13c3cad75 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/audiojak
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1e42da4254de6de5889ed7945f0a48e13c3cad75 -
Trigger Event:
push
-
Statement type: