Send emails via SMTP, free APIs, or privacy-focused services. Use when: (1) Sending notifications and alerts, (2) Automated reports and summaries, (3) User communication workflows, or (4) Error logging via email.
npx skills add https://github.com/besoeasy/open-skills --skill send-email-programmatically
Send emails via SMTP, free APIs (Mailgun, SendGrid free tier), or privacy-focused services. Essential for notifications, alerts, automated reports, and user communication.
Install options:
# Ubuntu/Debian
sudo apt-get install -y curl msmtp msmtp-mta
# macOS (postfix is pre-installed, or use msmtp)
brew install msmtp
# Node.js
npm install nodemailer
Send email using SMTP via curl (works with Gmail, Outlook, custom SMTP servers).
# Gmail SMTP example (requires app password)
SMTP_SERVER="smtp.gmail.com:587"
FROM_EMAIL="[email protected]"
TO_EMAIL="[email protected]"
SUBJECT="Test Email"
BODY="This is a test email sent via SMTP."
APP_PASSWORD="your-app-password"
# Send email
curl -v --url "smtp://${SMTP_SERVER}" \
--mail-from "${FROM_EMAIL}" \
--mail-rcpt "${TO_EMAIL}" \
--user "${FROM_EMAIL}:${APP_PASSWORD}" \
--upload-file - <<EOF
From: ${FROM_EMAIL}
To: ${TO_EMAIL}
Subject: ${SUBJECT}
${BODY}
EOF
# Outlook/Office365 SMTP
curl --url "smtp://smtp.office365.com:587" \
--mail-from "[email protected]" \
--mail-rcpt "[email protected]" \
--user "[email protected]:your-password" \
--ssl-reqd \
--upload-file - <<EOF
From: [email protected]
To: [email protected]
Subject: Hello from Outlook
This is an automated email.
EOF
Send email using Mailgun free tier (5,000 emails/month, no credit card required for sandbox).
# Set your Mailgun credentials
MAILGUN_API_KEY="your-mailgun-api-key"
MAILGUN_DOMAIN="sandbox123.mailgun.org" # or your verified domain
# Send email
curl -s --user "api:${MAILGUN_API_KEY}" \
"https://api.mailgun.net/v3/${MAILGUN_DOMAIN}/messages" \
-F from="Sender Name <mailgun@${MAILGUN_DOMAIN}>" \
-F to="[email protected]" \
-F subject="Hello from Mailgun" \
-F text="This is the plain text body" \
-F html="<h1>HTML Email</h1><p>This is the HTML body</p>"
# Send with attachment
curl -s --user "api:${MAILGUN_API_KEY}" \
"https://api.mailgun.net/v3/${MAILGUN_DOMAIN}/messages" \
-F from="notifications@${MAILGUN_DOMAIN}" \
-F to="[email protected]" \
-F subject="Report Attached" \
-F text="Please find the report attached." \
-F attachment=@./report.pdf
Node.js:
async function sendEmailMailgun(options) {
const { apiKey, domain, from, to, subject, text, html } = options;
const formData = new URLSearchParams({
from,
to,
subject,
text: text || '',
html: html || ''
});
const auth = 'Basic ' + Buffer.from(`api:${apiKey}`).toString('base64');
const res = await fetch(`https://api.mailgun.net/v3/${domain}/messages`, {
method: 'POST',
headers: {
'Authorization': auth,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formData
});
if (!res.ok) {
const error = await res.text();
throw new Error(`Mailgun API error: ${error}`);
}
return await res.json();
}
// Usage
// sendEmailMailgun({
// apiKey: 'your-mailgun-api-key',
// domain: 'sandbox123.mailgun.org',
// from: 'Sender <[email protected]>',
// to: '[email protected]',
// subject: 'Test Email',
// text: 'Plain text body',
// html: '<h1>HTML body</h1>'
// }).then(result => console.log('Email sent:', result));
Send email using SendGrid free tier (100 emails/day).
# Set your SendGrid API key
SENDGRID_API_KEY="your-sendgrid-api-key"
# Send email
curl -s --request POST \
--url "https://api.sendgrid.com/v3/mail/send" \
--header "Authorization: Bearer ${SENDGRID_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"personalizations": [{
"to": [{"email": "[email protected]"}],
"subject": "Hello from SendGrid"
}],
"from": {"email": "[email protected]", "name": "Sender Name"},
"content": [{
"type": "text/plain",
"value": "This is the email body."
}]
}'
# Send HTML email with attachment
curl -s --request POST \
--url "https://api.sendgrid.com/v3/mail/send" \
--header "Authorization: Bearer ${SENDGRID_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"personalizations": [{
"to": [{"email": "[email protected]"}]
}],
"from": {"email": "[email protected]"},
"subject": "Weekly Report",
"content": [{
"type": "text/html",
"value": "<h1>Weekly Report</h1><p>Attached is your report.</p>"
}],
"attachments": [{
"content": "'"$(base64 -w 0 report.pdf)"'",
"filename": "report.pdf",
"type": "application/pdf"
}]
}'
Node.js:
async function sendEmailSendGrid(options) {
const { apiKey, from, to, subject, text, html, attachments = [] } = options;
const payload = {
personalizations: [{
to: [{ email: to }],
subject
}],
from: { email: from },
content: [{
type: html ? 'text/html' : 'text/plain',
value: html || text
}]
};
if (attachments.length > 0) {
payload.attachments = attachments.map(att => ({
content: att.content, // base64 string
filename: att.filename,
type: att.type || 'application/octet-stream'
}));
}
const res = await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!res.ok) {
const error = await res.text();
throw new Error(`SendGrid API error: ${error}`);
}
return { success: true, status: res.status };
}
// Usage
// sendEmailSendGrid({
// apiKey: 'your-sendgrid-api-key',
// from: '[email protected]',
// to: '[email protected]',
// subject: 'Test Email',
// html: '<h1>Hello</h1><p>This is a test.</p>'
// }).then(result => console.log('Email sent:', result));
Send email using msmtp (lightweight SMTP client, good for automation).
# Configure msmtp (one-time setup)
cat > ~/.msmtprc <<EOF
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log
# Gmail account
account gmail
host smtp.gmail.com
port 587
from [email protected]
user [email protected]
password your-app-password
# Set default account
account default : gmail
EOF
chmod 600 ~/.msmtprc
# Send email
echo -e "Subject: Test Email\nFrom: [email protected]\nTo: [email protected]\n\nThis is the email body." | msmtp [email protected]
# Send email with file content
cat report.txt | msmtp -t <<EOF
To: [email protected]
From: [email protected]
Subject: Daily Report
$(cat report.txt)
EOF
# Send HTML email
msmtp [email protected] <<EOF
To: [email protected]
From: [email protected]
Subject: HTML Email
Content-Type: text/html
<html>
<body>
<h1>Hello</h1>
<p>This is an HTML email.</p>
</body>
</html>
EOF
Send email using Node.js nodemailer library (supports all SMTP servers).
Node.js:
const nodemailer = require('nodemailer');
async function sendEmail(config) {
const {
smtpHost,
smtpPort,
smtpUser,
smtpPassword,
from,
to,
subject,
text,
html,
attachments = []
} = config;
// Create transporter
const transporter = nodemailer.createTransport({
host: smtpHost,
port: smtpPort,
secure: smtpPort === 465, // true for 465, false for other ports
auth: {
user: smtpUser,
pass: smtpPassword
},
connectionTimeout: 10000
});
// Send email
const info = await transporter.sendMail({
from,
to,
subject,
text,
html,
attachments: attachments.map(att => ({
filename: att.filename,
path: att.path || undefined,
content: att.content || undefined
}))
});
return {
success: true,
messageId: info.messageId,
response: info.response
};
}
// Usage - Gmail
// sendEmail({
// smtpHost: 'smtp.gmail.com',
// smtpPort: 587,
// smtpUser: '[email protected]',
// smtpPassword: 'your-app-password',
// from: '"Sender Name" <[email protected]>',
// to: '[email protected]',
// subject: 'Hello',
// text: 'Plain text body',
// html: '<b>HTML body</b>',
// attachments: [
// { filename: 'report.pdf', path: './report.pdf' }
// ]
// }).then(result => console.log('Email sent:', result));
// Usage - Outlook
// sendEmail({
// smtpHost: 'smtp.office365.com',
// smtpPort: 587,
// smtpUser: '[email protected]',
// smtpPassword: 'your-password',
// from: '[email protected]',
// to: '[email protected]',
// subject: 'Test',
// text: 'This is a test email'
// });
Production-ready email sending with retry logic and error handling.
#!/bin/bash
send_email_with_retry() {
local TO="$1"
local SUBJECT="$2"
local BODY="$3"
local MAX_RETRIES=3
local RETRY_DELAY=5
for i in $(seq 1 $MAX_RETRIES); do
if curl -fsS --max-time 30 \
--url "smtp://smtp.gmail.com:587" \
--mail-from "[email protected]" \
--mail-rcpt "$TO" \
--user "[email protected]:app-password" \
--upload-file - <<EOF
From: [email protected]
To: $TO
Subject: $SUBJECT
$BODY
EOF
then
echo "Email sent successfully to $TO"
return 0
else
echo "Attempt $i failed, retrying in ${RETRY_DELAY}s..." >&2
sleep $RETRY_DELAY
fi
done
echo "Failed to send email after $MAX_RETRIES attempts" >&2
return 1
}
# Usage
send_email_with_retry "[email protected]" "Alert" "System CPU usage is high"
Node.js:
async function sendEmailWithRetry(config, maxRetries = 3) {
const { provider, ...emailConfig } = config;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
let result;
if (provider === 'mailgun') {
result = await sendEmailMailgun(emailConfig);
} else if (provider === 'sendgrid') {
result = await sendEmailSendGrid(emailConfig);
} else {
throw new Error(`Unknown provider: ${provider}`);
}
return { success: true, attempt, result };
} catch (err) {
console.error(`Attempt ${attempt} failed:`, err.message);
if (attempt === maxRetries) {
throw new Error(`Failed to send email after ${maxRetries} attempts: ${err.message}`);
}
// Exponential backoff
const delayMs = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
}
// Usage
// sendEmailWithRetry({
// provider: 'mailgun',
// apiKey: 'your-api-key',
// domain: 'sandbox123.mailgun.org',
// from: '[email protected]',
// to: '[email protected]',
// subject: 'Critical Alert',
// text: 'System requires attention'
// }, 3).then(result => console.log('Email sent:', result));
You have email sending capability via SMTP and free APIs. When a user asks to send an email:
1. Choose the best method based on requirements:
- **curl + SMTP** — For simple emails with Gmail/Outlook (requires app password)
- **Mailgun API** — For higher volume (5,000/month free, requires API key)
- **SendGrid API** — For moderate volume (100/day free, requires API key)
- **msmtp** — For automated scripts and cron jobs
- **nodemailer** — For Node.js applications with full SMTP support
2. For Gmail/Outlook SMTP:
- Gmail: smtp.gmail.com:587 (requires app password from Google Account settings)
- Outlook: smtp.office365.com:587
- Always use app passwords, never account passwords
3. For Mailgun:
- Free sandbox domain: 5,000 emails/month to authorized recipients
- Verified domain: Unlimited recipients (within free tier limits)
- No credit card required for sandbox
4. For SendGrid:
- Free tier: 100 emails/day
- Requires account signup and API key
5. Always:
- Validate recipient email format
- Use environment variables for credentials
- Implement retry logic (3 attempts with exponential backoff)
- Handle errors gracefully with clear messages
- Never log credentials or sensitive data
6. Security:
- Store SMTP passwords in ~/.msmtprc (chmod 600) or environment variables
- Use TLS/SSL for all SMTP connections
- Validate and sanitize email content to prevent injection
Error: "authentication failed" (Gmail)
Error: "530 5.7.0 Must issue a STARTTLS command first"
--ssl-reqd to curl command or use port 587/465Error: "554 5.7.1 Relay access denied"
Mailgun: "Free accounts are for test purposes only"
SendGrid: "403 Forbidden"
Emails going to spam:
Timeout errors:
Analyzes meeting transcripts and recordings to uncover behavioral patterns, communication insights, and actionable feedback. Identifies when you avoid conflict, use filler words, dominate conversations, or miss opportunities to listen. Perfect for professionals seeking to improve their communication and leadership skills.
Toolkit for creating animated GIFs optimized for Slack, with validators for size constraints and composable animation primitives. This skill applies when users request animated GIFs or emoji animations for Slack from descriptions like "make me a GIF for Slack of X doing Y".
Analyzes your recent Claude Code chat history to identify coding patterns, development gaps, and areas for improvement, curates relevant learning resources from HackerNews, and automatically sends a personalized growth report to your Slack DMs.
Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack.
A skill that creates new Claude skills and automatically shares them on Slack using Rube for seamless team collaboration and skill discovery.
Automate Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify, etc.) using agent-browser via Chrome DevTools Protocol. Use when the user needs to interact with an Electron app, automate a desktop app, connect to a running app, control a native app, or test an Electron application. Triggers include "automate Slack app", "control VS Code", "interact with Discord app", "test this Electron app", "connect to desktop app", or any task requiring automation of a native Electron application.
Prepare meeting materials with Notion context and Codex research; use when gathering context, drafting agendas/pre-reads, and tailoring materials to attendees.
Interactive daily standup/meeting update generator. Use when user says 'daily', 'standup', 'scrum update', 'status update', 'what did I do yesterday', 'prepare for meeting', 'morning update', or 'team sync'. Pulls activity from GitHub, Jira, and Claude Code session history. Conducts 4-question interview (yesterday, today, blockers, discussion topics) and generates formatted Markdown update.
Take besoeasy/send-email-programmatically from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm, brew, apt.
Without those the skill loads but fails at the first command.