AGENT SKILLBY BESOEASY
Send email programmatically
AT A GLANCE
Send email programmatically is an SEO skill for AI agents. Send emails via SMTP, free APIs, or privacy-focused services. It is published by besoeasy in the besoeasy/open-skills GitHub repository (131 stars) under the MIT license. Install it with: npx skills add besoeasy/open-skills --skill send-email-programmatically
- SOURCE REPOSITORY
- besoeasy/open-skills (opens GitHub in a new tab)
- REPO STARS
- SOURCE REPOSITORY
- LICENSE
- MIT
- SKILL.MD SIZE
- 23.1 KB860 LINES
QUICK ANSWER
- WHAT IT IS01
- 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. What sets it apart is how it narrows email marketing into one specific workflow rather than a broad, generic prompt.
- INPUTS02
- Context for email marketing: your goals, audience, constraints, and any source material the skill asks for.
- OUTPUT03
- A ready-to-use result for email marketing: the analysis, copy, or recommendations the agent produces.
INSTALL
One command adds this skill from its source repository.
npx skills add besoeasy/open-skills --skill send-email-programmaticallyREAD THE SKILL.MD BELOW BEFORE YOU RUN IT. SKILLS INSTRUCT YOUR AGENT.
SKILL.MD
The full file, as published in the source repository. Copy it, download it or open it on GitHub.
ON THIS PAGE · 27
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.
When to use#
- Use case 1: When the user asks to send email notifications or alerts
- Use case 2: When you need to deliver automated reports or summaries
- Use case 3: For error logging and monitoring alerts
- Use case 4: When building user communication workflows (confirmations, updates)
Required tools / APIs#
- curl — For API-based email sending (pre-installed on most systems)
- sendmail / msmtp — For SMTP email sending
- Mailgun API — Free tier: 5,000 emails/month (requires API key)
- SendGrid API — Free tier: 100 emails/day (requires API key)
- mail.tm — Temporary email API (no API key required)
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 nodemailerSkills#
send_email_via_smtp_curl#
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="your-email@gmail.com"
TO_EMAIL="recipient@example.com"
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 "your-email@outlook.com" \
--mail-rcpt "recipient@example.com" \
--user "your-email@outlook.com:your-password" \
--ssl-reqd \
--upload-file - <<EOF
From: your-email@outlook.com
To: recipient@example.com
Subject: Hello from Outlook
This is an automated email.
EOFsend_email_via_mailgun_api#
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="recipient@example.com" \
-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="user@example.com" \
-F subject="Report Attached" \
-F text="Please find the report attached." \
-F attachment=@./report.pdfNode.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 <mailgun@sandbox123.mailgun.org>',
// to: 'recipient@example.com',
// subject: 'Test Email',
// text: 'Plain text body',
// html: '<h1>HTML body</h1>'
// }).then(result => console.log('Email sent:', result));send_email_via_sendgrid_api#
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": "recipient@example.com"}],
"subject": "Hello from SendGrid"
}],
"from": {"email": "sender@example.com", "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": "user@example.com"}]
}],
"from": {"email": "notifications@example.com"},
"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: 'sender@example.com',
// to: 'recipient@example.com',
// subject: 'Test Email',
// html: '<h1>Hello</h1><p>This is a test.</p>'
// }).then(result => console.log('Email sent:', result));send_email_via_msmtp#
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 your-email@gmail.com
user your-email@gmail.com
password your-app-password
# Set default account
account default : gmail
EOF
chmod 600 ~/.msmtprc
# Send email
echo -e "Subject: Test Email\nFrom: your-email@gmail.com\nTo: recipient@example.com\n\nThis is the email body." | msmtp recipient@example.com
# Send email with file content
cat report.txt | msmtp -t <<EOF
To: recipient@example.com
From: sender@gmail.com
Subject: Daily Report
$(cat report.txt)
EOF
# Send HTML email
msmtp recipient@example.com <<EOF
To: recipient@example.com
From: sender@gmail.com
Subject: HTML Email
Content-Type: text/html
<html>
<body>
<h1>Hello</h1>
<p>This is an HTML email.</p>
</body>
</html>
EOFsend_email_nodejs_nodemailer#
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: 'your-email@gmail.com',
// smtpPassword: 'your-app-password',
// from: '"Sender Name" <your-email@gmail.com>',
// to: 'recipient@example.com',
// 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: 'your-email@outlook.com',
// smtpPassword: 'your-password',
// from: 'your-email@outlook.com',
// to: 'recipient@example.com',
// subject: 'Test',
// text: 'This is a test email'
// });advanced_email_with_retry#
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 "sender@gmail.com" \
--mail-rcpt "$TO" \
--user "sender@gmail.com:app-password" \
--upload-file - <<EOF
From: sender@gmail.com
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 "user@example.com" "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: 'sender@sandbox123.mailgun.org',
// to: 'recipient@example.com',
// subject: 'Critical Alert',
// text: 'System requires attention'
// }, 3).then(result => console.log('Email sent:', result));Rate limits / Best practices#
- ✅ Use app passwords — For Gmail/Outlook, create app-specific passwords instead of account passwords
- ✅ Free tier limits — Mailgun: 5,000/month, SendGrid: 100/day, Gmail: 500/day
- ✅ Retry logic — Implement exponential backoff for failed sends
- ✅ Error handling — Catch and log errors, don't expose credentials in logs
- ✅ Validate emails — Check recipient email format before sending
- ✅ SPF/DKIM — Configure DNS records for custom domains to avoid spam
- ⚠️ Never hardcode credentials — Use environment variables or secure vaults
- ⚠️ Rate limiting — Add delays between bulk sends (1-2 seconds)
- ⚠️ Bounce handling — Monitor bounces and remove invalid addresses
Agent prompt#
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 injectionTroubleshooting#
Error: "authentication failed" (Gmail)
- Symptom: SMTP auth fails with Gmail account password
- Solution: Generate an app password at https://myaccount.google.com/apppasswords (opens in a new tab) (requires 2FA enabled)
Error: "530 5.7.0 Must issue a STARTTLS command first"
- Symptom: Server requires TLS encryption
- Solution: Add
--ssl-reqdto curl command or use port 587/465
Error: "554 5.7.1 Relay access denied"
- Symptom: SMTP server rejects email relay
- Solution: Authenticate with correct credentials, ensure FROM email matches authenticated account
Mailgun: "Free accounts are for test purposes only"
- Symptom: Mailgun sandbox restricts recipients
- Solution: Add authorized recipients in Mailgun dashboard, or verify a custom domain
SendGrid: "403 Forbidden"
- Symptom: API key lacks permissions
- Solution: Create new API key with "Mail Send" permissions in SendGrid dashboard
Emails going to spam:
- Symptom: Recipients don't receive emails or they're in spam folder
- Solution: Configure SPF, DKIM, DMARC DNS records for custom domains; use verified sender emails
Timeout errors:
- Symptom: SMTP connection hangs or times out
- Solution: Check firewall allows outbound connections on ports 587/465; increase timeout to 30 seconds
See also#
- ../using-telegram-bot/SKILL.md (opens in a new tab) — Alternative notification method via Telegram
- ../nostr-logging-system/SKILL.md (opens in a new tab) — Decentralized logging alternative
- ../user-aks-for-report/SKILL.md (opens in a new tab) — Generate reports that can be emailed
Supporting file: skills/nostr-logging-system/SKILL.md#
Nostr Logging System#
Use Nostr as a distributed logging transport: publish non-sensitive logs publicly, and send sensitive logs privately to the admin via Nostr DM.
When to use#
- You want tamper-resistant, relay-distributed public operational logs.
- You want sensitive logs (errors with secrets, internal traces) delivered privately to an admin.
- You need a lightweight logging channel without centralized log infrastructure.
Required tools / APIs#
- Node.js 18+
nostr-sdklibrary
Install:
npm install nostr-sdkEnvironment variables:
# REQUIRED: admin Nostr public identity (npub or hex pubkey)
export ADMIN_NOSTR_PUBKEY="npub1..."
# REQUIRED for the logger identity (create if missing; see setup below)
export NOSTR_NSEC="nsec1..."
# Optional
export NOSTR_RELAYS="wss://relay.damus.io,wss://nos.lol,wss://relay.snort.social"Setup flow (must do first)#
- Ask the admin for their Nostr address (
npub/ public key). - Check whether you already have
NOSTR_NSECsaved. - If missing, generate a new keypair and save the
nsecfor future runs.
Generate and persist your nsec if missing (Node.js)#
// setup-nostr-identity.js
const fs = require('fs');
const path = require('path');
const { generateRandomNsec, nsecToPublic } = require('nostr-sdk');
function ensureNostrIdentity() {
const envPath = path.resolve(process.cwd(), '.env');
const envText = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
const fromProcess = process.env.NOSTR_NSEC;
const fromEnvFile = envText.match(/^NOSTR_NSEC=(.+)$/m)?.[1];
const currentNsec = fromProcess || fromEnvFile;
if (currentNsec && currentNsec.startsWith('nsec1')) {
console.log('NOSTR_NSEC already exists. Reusing saved key.');
return currentNsec;
}
const nsec = generateRandomNsec();
const pub = nsecToPublic(nsec);
const line = `NOSTR_NSEC=${nsec}`;
const nextEnv = envText.includes('NOSTR_NSEC=')
? envText.replace(/^NOSTR_NSEC=.*$/m, line)
: `${envText}${envText.endsWith('\n') || envText.length === 0 ? '' : '\n'}${line}\n`;
fs.writeFileSync(envPath, nextEnv, 'utf8');
console.log('Generated new Nostr identity. Saved NOSTR_NSEC to .env');
console.log('Your npub:', pub.npub);
return nsec;
}
ensureNostrIdentity();Run:
node setup-nostr-identity.jsSkills#
1. Public log event (non-sensitive)#
const { posttoNostr } = require('nostr-sdk');
async function logPublic(message, level = 'info') {
const tags = [
['t', 'logs'],
['t', 'public'],
['t', level]
];
return posttoNostr(`[PUBLIC_LOG] ${message}`, {
nsec: process.env.NOSTR_NSEC,
tags,
relays: null,
powDifficulty: 4
});
}
// Example:
// await logPublic('Worker started successfully', 'info');2. Sensitive log to admin DM#
const { sendMessageNIP17 } = require('nostr-sdk');
async function logSensitiveToAdmin(message) {
const admin = process.env.ADMIN_NOSTR_PUBKEY;
if (!admin) throw new Error('Missing ADMIN_NOSTR_PUBKEY');
return sendMessageNIP17(admin, `[SENSITIVE_LOG] ${message}`, {
nsec: process.env.NOSTR_NSEC
});
}
// Example:
// await logSensitiveToAdmin('DB auth retry failed for tenant=alpha');3. Route logs by sensitivity (single logger)#
const { posttoNostr, sendMessageNIP17 } = require('nostr-sdk');
async function logNostrEvent({ level = 'info', message, sensitive = false, context = {} }) {
if (!process.env.NOSTR_NSEC) throw new Error('Missing NOSTR_NSEC');
if (!process.env.ADMIN_NOSTR_PUBKEY) throw new Error('Missing ADMIN_NOSTR_PUBKEY');
const payload = JSON.stringify({
ts: new Date().toISOString(),
level,
message,
context
});
if (sensitive) {
return sendMessageNIP17(process.env.ADMIN_NOSTR_PUBKEY, `[SENSITIVE_LOG] ${payload}`, {
nsec: process.env.NOSTR_NSEC
});
}
return posttoNostr(`[PUBLIC_LOG] ${payload}`, {
nsec: process.env.NOSTR_NSEC,
tags: [['t', 'logs'], ['t', 'public'], ['t', level]],
relays: null,
powDifficulty: 4
});
}
// Example:
// await logNostrEvent({ level: 'info', message: 'Cron completed', sensitive: false });
// await logNostrEvent({ level: 'error', message: 'JWT parse failed', sensitive: true, context: { userId: 42 } });Agent prompt#
Use the Nostr Logging System skill.
Rules:
1) Ask for admin Nostr address first (npub/public key) and store as ADMIN_NOSTR_PUBKEY.
2) Check if NOSTR_NSEC already exists in environment/.env.
3) If missing, generate a new identity and persist NOSTR_NSEC for future runs.
4) Route logs:
- Non-sensitive -> public Nostr note with tags logs/public/<level>
- Sensitive -> private DM to ADMIN_NOSTR_PUBKEY using NIP-17
5) Never publish secrets in public notes.Best practices#
- Redact secrets (tokens, private keys, passwords) before logging.
- Treat anything user-identifying as sensitive by default.
- Add stable tags (
logs,service-name,env) for easier filtering. - Use multiple relays for better delivery and resilience.
- Rotate logger identity keys if compromised.
Troubleshooting#
Missing ADMIN_NOSTR_PUBKEY: Ask admin fornpub/public key and export it.Missing NOSTR_NSEC: Run the setup script to generate and persist identity.- Low publish success: Add more relays or retry with lower POW difficulty.
- DM not received: Confirm admin key is correct and relay supports DMs.
See also#
Supporting file: skills/using-telegram-bot/SKILL.md#
Telegram (Telegraf) Skill — Node.js#
Short guide to build Telegram bots with telegraf (Node.js).
Overview#
- Library: https://github.com/telegraf/telegraf (opens in a new tab)
- Install:
npm install telegraf - Get a bot token from BotFather and store it in
BOT_TOKEN.
Minimal polling bot#
// bot.js
const { Telegraf, Markup } = require('telegraf');
const bot = new Telegraf(process.env.BOT_TOKEN);
bot.start(ctx => ctx.reply('Welcome! I can help with commands.'));
bot.command('echo', ctx => {
const text = ctx.message.text.split(' ').slice(1).join(' ');
ctx.reply(text || 'usage: /echo your message');
});
bot.on('text', ctx => ctx.reply(`You said: ${ctx.message.text}`));
bot.launch();
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));Run:
BOT_TOKEN=123:ABC node bot.jsSend media and files#
// send photo
await ctx.replyWithPhoto('https://example.com/image.jpg', { caption: 'Nice pic' });
// send document
await ctx.replyWithDocument('https://example.com/file.pdf');Inline keyboards and callbacks#
// show inline buttons
await ctx.reply('Choose:', Markup.inlineKeyboard([
Markup.button.callback('OK', 'ok'),
Markup.button.callback('Cancel', 'cancel')
]));
bot.action('ok', ctx => ctx.reply('You pressed OK'));
bot.action('cancel', ctx => ctx.reply('Cancelled'));Webhook (Express) example#
const express = require('express');
const { Telegraf } = require('telegraf');
const bot = new Telegraf(process.env.BOT_TOKEN);
const app = express();
app.use(bot.webhookCallback('/telegraf'));
bot.telegram.setWebhook(`${process.env.PUBLIC_URL}/telegraf`);
app.listen(process.env.PORT || 3000);Use webhooks for production deployments (faster, lower resource use).
Error handling#
bot.catch((err, ctx) => {
console.error('Bot error', err);
});Tips#
- Use environment variables for tokens and URLs.
- Respect Telegram rate limits (avoid flooding large groups).
- For local testing, use polling; for deployment use webhooks behind HTTPS.
- Add
NODE_ENV=productionand graceful shutdown hooks for reliability.
This doc shows the most common Telegraf patterns: start/command handlers, text handlers, media, inline buttons, webhook setup, and error handling.
FAQ
07 QUESTIONS
01What is the Send email programmatically agent skill?
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. It is listed under SEO and email in the 100xLab agent skills directory.
02How do I install Send email programmatically in Cursor, Claude Code, or Codex?
Run npx skills add besoeasy/open-skills --skill send-email-programmatically in the project where you want it, then ask your agent for the skill by name. The --skill flag installs only Send email programmatically, not every skill in the repository.
03What does the Send email programmatically SKILL.md cover?
The SKILL.md includes sections on When to use, Required tools / APIs, Skills, send_email_via_smtp_curl, send_email_via_mailgun_api and send_email_via_sendgrid_api. Read the full text on this page or in plain markdown at /resources/agent-skills/besoeasy-open-skills--send-email-programmatically--1bg1ano/raw.
04Which agents can use Send email programmatically?
Send email programmatically installs with the cross-agent skills installer (npx skills add), which sets it up for agents such as Claude Code, Cursor and Codex. Each agent loads the SKILL.md when you ask for the skill by name.
05What license is Send email programmatically under?
The besoeasy/open-skills repository declares the MIT license. Read the license file in the repository before you copy or redistribute the skill.
06Is Send email programmatically free to use?
Reading, copying and installing Send email programmatically costs nothing: it is published in a public GitHub repository under the MIT license. Reuse and redistribution follow that license.
07Where is the source of Send email programmatically?
Send email programmatically comes from https://github.com/besoeasy/open-skills, which has 131 GitHub stars. The SKILL.md is at https://github.com/besoeasy/open-skills/blob/HEAD/skills/send-email-programmatically/SKILL.md. The author is besoeasy.
Need an agent workflow built around your stack?
Tell us the workflow you want automated. We reply within 4 working hours (IST).
ATTRIBUTION
Send email programmatically was written by its original authors and is published in besoeasy/open-skills (opens GitHub in a new tab) under the MIT license. All rights stay with the original authors.
100xLab did not write, maintain or endorse this skill. This page lists it for discovery. Repo stars belong to the source repository, not to the skill. Review any skill before you install it.