Mail Merge Guide
Send every row of a spreadsheet its own PDF (an invoice, statement, certificate or letter) as an email attachment, from your own mailbox.
This guide walks through a first campaign. For every field and error, see the mail merge API reference.
What you need
- A template. Design the document once in the dashboard under Templates. Each
{{field}}in it becomes a column you fill from the spreadsheet. - A spreadsheet. CSV or Excel (
.xlsx), with a header row and one row per recipient, including a column with each email address. - A sender. The SMTP details of the mailbox the emails should come from.
1. Add a sender
In the dashboard, open Mail merge → Senders → Add sender. Pick your provider and the server settings fill in.
| Provider | Server | Port | Password to use |
|---|---|---|---|
| Gmail / Google Workspace | smtp.gmail.com | 587 | An app password, not your normal password |
| Microsoft 365 / Outlook | smtp.office365.com | 587 | Your password; SMTP AUTH must be turned on for the mailbox in the Microsoft 365 admin center |
| Zoho Mail | smtp.zoho.com | 465 | An app-specific password if two-factor sign-in is on |
| Brevo | smtp-relay.brevo.com | 587 | An SMTP key from SMTP & API |
| SendGrid | smtp.sendgrid.net | 587 | Username apikey, password your API key |
After saving, send the test email: it arrives with a small PDF attached. Only then can the sender run campaigns. We store the password encrypted and never show it again.
Your mailbox's own daily limit applies: about 500 a day on Gmail, 2,000 on Google Workspace, and 10,000 recipients on Microsoft 365. For bigger lists, use a sending service such as Brevo or SendGrid as the sender. If your provider limits the rate, set Emails per minute on the sender and campaigns will stay under it.
2. Prepare the spreadsheet
Email,Name,Invoice #,Amount,Due
ann@example.com,Ann Lee,INV-1001,"1,204.50",2026-10-31
bo@example.com,Bo Chan,INV-1002,88.00,2026-10-31
- Numbers.
1,204.50,$12.00and20%are read as numbers. - Dates. Write them as
2026-10-31. Excel dates are converted for you. - Repeated addresses get one email (turn this off with
options.dedupe: falsein the API). - Limits. Up to 100 rows per campaign. For more, choose Request a higher limit under Mail merge: say how many you need (up to 10,000) and what you are sending. We review requests within one business day.
3. Create the campaign
Mail merge → New campaign walks through six steps:
- Template. Pick the document every recipient gets.
- Recipients. Upload the file. The first rows are shown so you can check it was read correctly.
- Columns. Match each template field to a column. Matches are suggested from the column names.
- Email. Choose the sender and write the subject and message. The
{ }button inserts variables: template fields, or any column (Invoice #becomes{{invoice}}). - Review. Every row is checked: bad addresses, missing required values and duplicates are listed with their row numbers. Step through rows to see each email and PDF exactly as it will be sent, then send one to yourself.
- Send. Confirm that every recipient agreed to receive these emails, and start.
You can close the page while it sends. Progress shows under Mail merge, and we email you when it finishes.
4. Read the results
The campaign page and the report CSV show what happened to every row:
| Result | What it means | Charged |
|---|---|---|
| Sent | Your mail server accepted it | Yes |
| Rejected | Your server refused that address | No |
| Failed | The PDF could not be made, or the server refused the message | No |
| Unconfirmed | The connection dropped just after the message was sent. It may have arrived, so it is not sent again | No |
| Skipped | Invalid or duplicate row, or the campaign stopped first | No |
If your mail server rejects the password mid-campaign (for example after a password change), the campaign stops, the sender is marked failing, and the unsent rows are skipped. Fix the sender, send it a test, then send the skipped rows in a new campaign.
Doing it with the API
Everything after adding the sender works with an API key: create, preview, test, start, follow progress and download the report.
curl -X POST https://api.podpdf.com/mailmerge/campaigns \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"template_id": "YOUR_TEMPLATE_ID",
"connection_id": "YOUR_CONNECTION_ID",
"input": { "csv": "Email,Name,Amount\nann@example.com,Ann Lee,1204.50\n" },
"mapping": { "customer.name": "Name", "amount": "Amount" },
"email": {
"subject": "Your statement, {{customer.name}}",
"body_text": "Hello {{customer.name}},\nYour statement for {{amount}} is attached."
},
"start": true,
"consent": { "confirmed": true }
}'
Then poll GET /jobs/{campaign_id}, or subscribe a webhook to mailmerge.campaign.completed and mailmerge.campaign.partial.
Good sending practice
- Consent. Only email people who agreed to hear from you: for invoices and statements, your customers.
- Unsubscribe. Add an unsubscribe link for anything that is not strictly transactional. The Unsubscribe link setting adds the
List-Unsubscribeheader mail providers look for. - Test first. Send yourself a test and check it in Gmail and Outlook before a large campaign.
- Authenticate your domain. Make sure your domain's SPF and DKIM records cover your mail provider, so campaigns land in the inbox rather than spam.