@devalxui/kova-mail
v1.0.1
Published
The last email toolkit you'll ever need. Send, receive, filter, auto-reply, track opens, schedule, and manage emails across Gmail, Outlook, and any IMAP/SMTP provider.
Maintainers
Readme
kova-mail
The last email toolkit you'll ever need.
Send, receive, filter, auto-reply, track opens, schedule, and manage emails — across Gmail, Outlook, and any IMAP/SMTP provider.
Why kova-mail?
Most email libraries do one thing. kova-mail does everything:
- Send — single, bulk, templated, scheduled, with attachments
- Receive — read inbox, fetch unread, search across folders
- Filter — 7 operators, AND/OR logic, chain rules
- Auto-reply — rule-based automatic responses with cooldowns
- Track — open tracking via invisible pixel, stats dashboard
- Schedule — send later with cancel support
- Templates —
{{variable}}interpolation engine - Multi-provider — Gmail, Outlook, any SMTP/IMAP server
One API. Every email feature you need.
Install
npm install kova-mailQuick Start
import { KovaMail } from "kova-mail";
const mail = new KovaMail({
type: "smtp",
config: {
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: { user: "[email protected]", pass: "your-app-password" },
},
});
// Send an email
await mail.send({
from: "[email protected]",
to: "[email protected]",
subject: "Let's talk growth",
html: "<h1>Hey!</h1><p>Ready to scale?</p>",
});Providers
Gmail (SMTP)
const mail = new KovaMail({
type: "smtp",
config: {
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: { user: "[email protected]", pass: "your-app-password" },
},
});Getting an App Password:
- Enable 2-Step Verification at https://myaccount.google.com/security
- Go to https://myaccount.google.com/apppasswords
- Generate a password for "Mail"
Gmail (OAuth2)
const mail = new KovaMail({
type: "gmail",
config: {
clientId: "your-client-id.apps.googleusercontent.com",
clientSecret: "your-client-secret",
refreshToken: "your-refresh-token",
from: "[email protected]",
},
});Outlook / Microsoft 365
const mail = new KovaMail({
type: "outlook",
config: {
clientId: "your-azure-app-id",
clientSecret: "your-azure-secret",
tenantId: "your-tenant-id",
refreshToken: "your-refresh-token",
},
});Any SMTP Server
const mail = new KovaMail({
type: "smtp",
config: {
host: "mail.yourdomain.com",
port: 587,
secure: false,
auth: { user: "[email protected]", pass: "password" },
},
});Yahoo Mail
const mail = new KovaMail({
type: "smtp",
config: {
host: "smtp.mail.yahoo.com",
port: 465,
secure: true,
auth: { user: "[email protected]", pass: "your-app-password" },
},
});Zoho Mail
const mail = new KovaMail({
type: "smtp",
config: {
host: "smtp.zoho.com",
port: 465,
secure: true,
auth: { user: "[email protected]", pass: "password" },
},
});SendGrid
const mail = new KovaMail({
type: "smtp",
config: {
host: "smtp.sendgrid.net",
port: 465,
secure: true,
auth: { user: "apikey", pass: "SG.your-sendgrid-api-key" },
},
});Amazon SES
const mail = new KovaMail({
type: "smtp",
config: {
host: "email-smtp.us-east-1.amazonaws.com",
port: 465,
secure: true,
auth: { user: "your-ses-smtp-user", pass: "your-ses-smtp-password" },
},
});Sending Emails
Basic Send
await mail.send({
from: "[email protected]",
to: "[email protected]",
subject: "Hello",
html: "<h1>Welcome!</h1>",
text: "Welcome!", // plain text fallback
});Multiple Recipients
await mail.send({
from: "[email protected]",
to: ["[email protected]", "[email protected]"],
cc: "[email protected]",
bcc: ["[email protected]"],
subject: "Team Update",
html: "<p>Here's the latest...</p>",
});With Attachments
import { readFileSync } from "fs";
await mail.send({
from: "[email protected]",
to: "[email protected]",
subject: "Proposal Attached",
html: "<p>Please find the proposal attached.</p>",
attachments: [
{
filename: "proposal.pdf",
content: readFileSync("./proposal.pdf"),
contentType: "application/pdf",
},
{
filename: "logo.png",
path: "./assets/logo.png", // or use file path
},
],
});Reply-To
await mail.send({
from: "[email protected]",
to: "[email protected]",
replyTo: "[email protected]",
subject: "Your Order",
html: "<p>Thanks for your order!</p>",
});Priority
await mail.send(
{ from: "[email protected]", to: "[email protected]", subject: "URGENT", html: "<p>Read now!</p>" },
{ priority: "high" }
);Bulk Send
const results = await mail.sendBulk([
{ email: { from: "[email protected]", to: "[email protected]", subject: "Hey", html: "<p>Hi!</p>" } },
{ email: { from: "[email protected]", to: "[email protected]", subject: "Hey", html: "<p>Hi!</p>" } },
{ email: { from: "[email protected]", to: "[email protected]", subject: "Hey", html: "<p>Hi!</p>" } },
]);
// Check results
results.forEach((r, i) => {
console.log(`Email ${i + 1}: ${r.success ? "Sent" : "Failed: " + r.error}`);
});Bulk sends are automatically rate-limited (100ms between emails for SMTP, 200ms for Gmail/Outlook) to avoid provider throttling.
Templates
Register a Template
mail.registerTemplate("welcome", {
subject: "Welcome to {{company}}, {{name}}!",
html: `
<div style="font-family: sans-serif; max-width: 600px;">
<h1>Hey {{name}}!</h1>
<p>Welcome to <strong>{{company}}</strong>. We're excited to have you.</p>
<p>Your account is ready. Here's what's next:</p>
<ul>
<li>Book your strategy call</li>
<li>Share your brand assets</li>
<li>We'll build your custom plan</li>
</ul>
<p>— The {{company}} Team</p>
</div>
`,
text: "Hey {{name}}! Welcome to {{company}}.",
});Send with Template
await mail.send(
{ from: "[email protected]", to: "[email protected]", subject: "", html: "" },
{
template: "welcome",
templateVars: { name: "Jake", company: "KOVA" },
}
);Multiple Templates
// Follow-up template
mail.registerTemplate("follow-up", {
subject: "Following up, {{name}}",
html: `<p>Hey {{name}}, just checking in about {{topic}}. Let me know if you have any questions.</p>`,
});
// Proposal template
mail.registerTemplate("proposal", {
subject: "Your Custom Marketing Proposal — {{company}}",
html: `
<h2>{{company}} Marketing Proposal</h2>
<p>Hey {{name}},</p>
<p>Based on our call, here's what we recommend:</p>
<p><strong>Monthly Investment:</strong> {{price}}</p>
<p><strong>Services:</strong> {{services}}</p>
<p>Ready to get started? Reply to this email.</p>
`,
});
// Invoice template
mail.registerTemplate("invoice", {
subject: "Invoice #{{invoiceNumber}} — {{company}}",
html: `
<h2>Invoice #{{invoiceNumber}}</h2>
<p>Amount Due: <strong>{{amount}}</strong></p>
<p>Due Date: {{dueDate}}</p>
<p>Pay here: {{paymentLink}}</p>
`,
});
// Cold outreach
mail.registerTemplate("cold-outreach", {
subject: "Quick question about {{company}}",
html: `
<p>Hey {{name}},</p>
<p>I came across {{company}} and noticed you're in the {{industry}} space. We recently helped a similar brand increase their ROAS by 3x in 90 days.</p>
<p>Would you be open to a quick 15-minute chat this week?</p>
<p>— {{senderName}}</p>
`,
});Template Variables
Variables use {{double_braces}} syntax. If a variable isn't provided, the placeholder stays as-is:
// These vars are available:
{ name: "Jake", company: "Pulse Fitness" }
// Template: "Hey {{name}} from {{company}}, re: {{topic}}"
// Result: "Hey Jake from Pulse Fitness, re: {{topic}}"
// {{topic}} stays because it wasn't providedManage Templates
mail.registerTemplate("test", { subject: "Test", html: "<p>Test</p>" });
// List all
const all = mail.templates; // not exposed yet, use registerTemplate
// Templates are stored in memory — register them on app startupReceiving Emails
Setup with IMAP
const mail = new KovaMail(
{ type: "smtp", config: { /* sending config */ } },
{
type: "imap",
config: {
host: "imap.gmail.com",
port: 993,
tls: true,
auth: { user: "[email protected]", pass: "your-app-password" },
},
}
);Setup with Gmail API
const mail = new KovaMail(
{ type: "gmail", config: { /* OAuth config */ } },
{
type: "gmail",
config: {
clientId: "your-client-id",
clientSecret: "your-secret",
refreshToken: "your-token",
},
}
);Read Inbox
// Latest 20 emails
const inbox = await mail.inbox(20);
// With offset (pagination)
const page2 = await mail.inbox(20, 20);
// Unread only
const unread = await mail.unread();Search
// Search by keyword (checks from, subject, body)
const results = await mail.search("proposal");
// Gmail-specific search queries
const results = await mail.search("from:[email protected] subject:invoice");
const results = await mail.search("has:attachment after:2026/01/01");Email Object
interface Email {
id?: string; // message ID
from: string; // sender
to: string | string[]; // recipients
cc?: string | string[];
bcc?: string | string[];
subject: string;
text?: string; // plain text body
html?: string; // HTML body
date?: Date; // send date
read?: boolean; // read status
starred?: boolean;
labels?: string[]; // Gmail labels
attachments?: EmailAttachment[];
}Filtering
Filter Rules
const emails = await mail.inbox(100);
// Filter by sender
const fromClients = mail.filterEmails(emails, [
{ field: "from", operator: "contains", value: "@client.com" },
]);
// Filter by subject
const proposals = mail.filterEmails(emails, [
{ field: "subject", operator: "contains", value: "proposal" },
]);
// Filter by date
const thisWeek = mail.filterEmails(emails, [
{ field: "date", operator: "after", value: "2026-03-25" },
]);
// Regex filter
const invoices = mail.filterEmails(emails, [
{ field: "subject", operator: "regex", value: "invoice\\s*#?\\d+" },
]);Available Operators
| Operator | Description | Example |
|----------|-------------|---------|
| contains | Case-insensitive substring match | "proposal" |
| equals | Exact match (case-insensitive) | "[email protected]" |
| startsWith | Starts with | "Re:" |
| endsWith | Ends with | "@gmail.com" |
| regex | Regular expression | "invoice\\s*#\\d+" |
| before | Date before | "2026-04-01" |
| after | Date after | "2026-03-01" |
Available Fields
| Field | What it matches |
|-------|----------------|
| from | Sender email |
| to | Recipient email(s) |
| subject | Email subject line |
| body | Email body (text or HTML) |
| date | Send date |
| labels | Gmail labels |
Combine Rules (AND)
// Must match ALL rules
const results = mail.filterEmails(emails, [
{ field: "from", operator: "contains", value: "client" },
{ field: "subject", operator: "contains", value: "urgent" },
{ field: "date", operator: "after", value: "2026-03-01" },
]);OR Logic
import { EmailFilter } from "kova-mail";
const filter = new EmailFilter();
const email = emails[0];
// Check if email matches ANY rule
const isImportant = filter.matchesAny(email, [
{ field: "from", operator: "contains", value: "ceo@" },
{ field: "subject", operator: "contains", value: "urgent" },
{ field: "subject", operator: "contains", value: "asap" },
]);Auto-Reply
Basic Auto-Reply
mail.addAutoReply({
name: "out-of-office",
filters: [
{ field: "to", operator: "contains", value: "[email protected]" },
],
reply: {
subject: "Out of Office",
html: "<p>Thanks for your email! I'm currently out of office and will respond within 24 hours.</p>",
},
enabled: true,
cooldownMinutes: 60, // don't reply to same sender within 60 min
});Auto-Reply with Template
mail.registerTemplate("auto-response", {
subject: "Re: {{originalSubject}}",
html: `
<p>Hey! Thanks for reaching out.</p>
<p>We've received your message and our team will get back to you within 2 hours during business hours (Mon-Fri 9am-6pm EST).</p>
<p>— KOVA Team</p>
`,
});
mail.addAutoReply({
name: "business-hours",
filters: [
{ field: "from", operator: "regex", value: ".*" }, // match all
],
reply: {
template: "auto-response",
},
enabled: true,
cooldownMinutes: 120,
maxRepliesPerSender: 3, // max 3 auto-replies per sender
});Process Auto-Replies
// Call this on a schedule (e.g., every 5 minutes)
const results = await mail.processAutoReplies();
console.log(`Sent ${results.length} auto-replies`);Manage Rules
mail.addAutoReply({ name: "rule1", /* ... */ });
mail.removeAutoReply("rule1");Open Tracking
Track Email Opens
const result = await mail.send(
{
from: "[email protected]",
to: "[email protected]",
subject: "Your Proposal",
html: "<h2>Here's your proposal</h2><p>See attached.</p>",
},
{ trackOpens: true }
);
console.log(result.trackingId); // "1711234567890-abc123"Handle Tracking Pixel
Set up an endpoint in your app to serve the tracking pixel:
// Next.js API route example
export async function GET(req: Request) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
if (id) mail.handleTrackingPixel(id);
// Return 1x1 transparent PNG
return new Response(Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", "base64"), {
headers: { "Content-Type": "image/png", "Cache-Control": "no-store" },
});
}Check Stats
const stats = mail.getOpenStats();
stats.forEach((data, trackingId) => {
console.log(`${trackingId}: opened ${data.count} times, first at ${data.openedAt}`);
});Scheduling
Send Later
// Send in 1 hour
await mail.send(
{ from: "[email protected]", to: "[email protected]", subject: "Follow up", html: "<p>Hey!</p>" },
{ scheduledAt: new Date(Date.now() + 60 * 60 * 1000) }
);
// Send tomorrow at 9am
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(9, 0, 0, 0);
await mail.send(
{ from: "[email protected]", to: "[email protected]", subject: "Good morning", html: "<p>Ready to chat?</p>" },
{ scheduledAt: tomorrow }
);View & Cancel Scheduled
// View all scheduled
const scheduled = mail.getScheduled();
scheduled.forEach((email, id) => {
console.log(`${id}: "${email.subject}" → ${email.scheduledAt}`);
});
// Cancel a scheduled email
mail.cancelScheduled("sched-1711234567890-abc123");Advanced Usage
Error Handling
const result = await mail.send({ /* ... */ });
if (!result.success) {
console.error("Failed:", result.error);
// Common errors:
// - "Invalid login" → wrong credentials
// - "Connection timeout" → server unreachable
// - "Rate limit exceeded" → slow down
}Cold Outreach Campaign
mail.registerTemplate("cold-v1", {
subject: "Quick question about {{company}}",
html: `<p>Hey {{name}},</p><p>I noticed {{company}} could benefit from better ad performance. We helped a similar brand 3x their ROAS in 90 days.</p><p>Worth a quick chat?</p>`,
});
const leads = [
{ name: "Jake", company: "Pulse Fitness", email: "[email protected]" },
{ name: "Mia", company: "Solara Skin", email: "[email protected]" },
{ name: "Tom", company: "GreenLeaf", email: "[email protected]" },
];
const results = await mail.sendBulk(
leads.map((lead) => ({
email: { from: "[email protected]", to: lead.email, subject: "", html: "" },
options: {
template: "cold-v1",
templateVars: { name: lead.name, company: lead.company },
trackOpens: true,
},
}))
);
console.log(`Sent: ${results.filter((r) => r.success).length}/${results.length}`);Inbox Monitoring Loop
// Check for new emails every 5 minutes
setInterval(async () => {
const unread = await mail.unread();
// Auto-categorize
const urgent = mail.filterEmails(unread, [
{ field: "subject", operator: "regex", value: "(urgent|asap|emergency)" },
]);
const invoices = mail.filterEmails(unread, [
{ field: "subject", operator: "contains", value: "invoice" },
]);
console.log(`${unread.length} unread, ${urgent.length} urgent, ${invoices.length} invoices`);
// Process auto-replies
await mail.processAutoReplies();
}, 5 * 60 * 1000);API Reference
KovaMail
| Method | Description |
|--------|-------------|
| send(email, options?) | Send a single email |
| sendBulk(emails) | Send multiple emails with rate limiting |
| inbox(limit?, offset?) | Fetch inbox emails |
| unread() | Fetch unread emails |
| search(query) | Search emails |
| filterEmails(emails, rules) | Filter email array by rules |
| registerTemplate(name, template) | Register a reusable template |
| addAutoReply(rule) | Add an auto-reply rule |
| removeAutoReply(name) | Remove an auto-reply rule |
| processAutoReplies() | Process and send auto-replies |
| handleTrackingPixel(id) | Record an email open |
| getOpenStats() | Get open tracking statistics |
| getScheduled() | Get all scheduled emails |
| cancelScheduled(id) | Cancel a scheduled email |
EmailFilter
| Method | Description |
|--------|-------------|
| apply(emails, rules) | Filter emails matching ALL rules |
| matchesAll(email, rules) | Check if email matches all rules |
| matchesAny(email, rules) | Check if email matches any rule |
TemplateEngine
| Method | Description |
|--------|-------------|
| register(name, template) | Register a template |
| apply(email, name, vars) | Apply template to email |
| list() | List all registered templates |
| get(name) | Get a template by name |
| remove(name) | Remove a template |
OpenTracker
| Method | Description |
|--------|-------------|
| injectPixel(html, id) | Inject tracking pixel into HTML |
| recordOpen(id) | Record an open event |
| isOpened(id) | Check if email was opened |
| getOpenCount(id) | Get open count for tracking ID |
| getStats() | Get all tracking statistics |
TypeScript
kova-mail is written in TypeScript and ships with full type definitions:
import type {
Email,
EmailOptions,
EmailResult,
EmailAttachment,
SmtpConfig,
GmailConfig,
OutlookConfig,
ImapConfig,
FilterRule,
AutoReplyRule,
Template,
TemplateVars,
ScheduledEmail,
} from "kova-mail";Contributing
git clone https://github.com/AlexanderGese/kova-mail.git
cd kova-mail
npm install
npm run build
npm testLicense
MIT — use it however you want.
Built by KOVA
