metigan
v2.3.0
Published
Official Metigan SDK - Email, OTP, Transactional, Forms, Contacts, and Audiences management with built-in security features
Maintainers
Readme
Metigan SDK
The official Metigan library for Node.js and browsers. Send emails, manage forms, contacts, and audiences with ease.
📦 Installation
npm install metigan
# or
yarn add metigan🚀 Quick Start
import Metigan from 'metigan';
// Initialize the client with all features
const metigan = new Metigan({
apiKey: 'your-api-key'
});
// Send email
await metigan.email.sendEmail({
from: 'Your Company <[email protected]>',
recipients: ['[email protected]'],
subject: 'Welcome!',
content: '<h1>Hello!</h1><p>Thank you for signing up.</p>'
});
// Send OTP (fast lane)
await metigan.email.sendOtp({
from: 'Your Company <[email protected]>',
to: '[email protected]',
code: '348921',
appName: 'Metigan',
expiresInMinutes: 10
});
// Send transactional email (fast lane)
await metigan.email.sendTransactional({
from: 'Your Company <[email protected]>',
to: '[email protected]',
subject: 'Your receipt',
content: '<p>Thanks for your purchase.</p>'
});
// Submit form
await metigan.forms.submit({
formId: 'form-123',
data: {
email: '[email protected]',
name: 'John Doe'
}
});
// Create contact
await metigan.contacts.create({
email: '[email protected]',
firstName: 'Jane',
audienceId: 'audience-456'
});📧 Email Module
Basic Send
const response = await metigan.email.sendEmail({
from: 'Your Company <[email protected]>',
recipients: ['[email protected]'],
subject: 'Email Subject',
content: '<h1>HTML Content</h1>'
});With Attachments (Node.js)
import fs from 'fs';
const response = await metigan.email.sendEmail({
from: '[email protected]',
recipients: ['[email protected]'],
subject: 'Important Document',
content: 'Please find the document attached.',
attachments: [
{
buffer: fs.readFileSync('./document.pdf'),
originalname: 'document.pdf',
mimetype: 'application/pdf'
}
]
});With CC and BCC
await metigan.email.sendEmail({
from: '[email protected]',
recipients: ['[email protected]'],
subject: 'Meeting',
content: 'Email content',
cc: ['[email protected]'],
bcc: ['[email protected]'],
replyTo: '[email protected]'
});OTP Send (Fast Lane)
await metigan.email.sendOtp({
from: 'Your Company <[email protected]>',
to: '[email protected]',
code: '482193',
appName: 'Metigan',
expiresInMinutes: 10
});Transactional Send (Fast Lane)
await metigan.email.sendTransactional({
from: 'Your Company <[email protected]>',
to: '[email protected]',
subject: 'Password changed',
content: '<p>Your password was updated successfully.</p>'
});📋 Forms Module
Submit Response
const response = await metigan.forms.submit({
formId: 'form-123', // or form slug
data: {
'field-email': '[email protected]',
'field-name': 'John Doe',
'field-message': 'Hello, I would like more information.'
}
});
console.log(response.message); // "Thank you for your submission!"Get Public Form
// By slug (for public display)
const form = await metigan.forms.getPublicForm('my-form');
console.log(form.title);
console.log(form.fields);List Forms
const { forms, pagination } = await metigan.forms.listForms({
page: 1,
limit: 10
});
forms.forEach(form => {
console.log(`${form.title} - ${form.analytics?.submissions || 0} responses`);
});Create Form
const newForm = await metigan.forms.createForm({
title: 'Contact Form',
description: 'Get in touch with us',
fields: [
{
id: 'field-email',
type: 'email',
label: 'Your Email',
required: true
},
{
id: 'field-name',
type: 'text',
label: 'Your Name',
required: true
},
{
id: 'field-subject',
type: 'select',
label: 'Subject',
options: ['Support', 'Sales', 'Partnerships']
},
{
id: 'field-message',
type: 'textarea',
label: 'Message',
required: true
}
],
settings: {
successMessage: 'Thank you! We will get back to you soon.',
notifyEmail: '[email protected]'
}
});Publish Form
const { publishedUrl, slug } = await metigan.forms.publishForm('form-123', 'contact');
console.log(`Form published at: ${publishedUrl}`);Form Analytics
const analytics = await metigan.forms.getAnalytics('form-123');
console.log(`Views: ${analytics.views}`);
console.log(`Submissions: ${analytics.submissions}`);
console.log(`Conversion rate: ${analytics.conversionRate}%`);👥 Contacts Module
Create Contact
const contact = await metigan.contacts.create({
email: '[email protected]',
firstName: 'Jane',
lastName: 'Doe',
audienceId: 'audience-123',
tags: ['customer', 'newsletter']
});Get Contact
// By ID
const contact = await metigan.contacts.get('contact-456');
// By email
const contact = await metigan.contacts.getByEmail('[email protected]', 'audience-123');Update Contact
const updated = await metigan.contacts.update('contact-456', {
firstName: 'Jane Marie',
tags: ['customer', 'vip']
});Manage Subscription
// Unsubscribe
await metigan.contacts.unsubscribe('contact-456');
// Resubscribe
await metigan.contacts.subscribe('contact-456');Manage Tags
// Add tags
await metigan.contacts.addTags('contact-456', ['vip', 'black-friday']);
// Remove tags
await metigan.contacts.removeTags('contact-456', ['test']);List Contacts
const { contacts, pagination } = await metigan.contacts.list({
audienceId: 'audience-123',
status: 'subscribed',
tag: 'customer',
page: 1,
limit: 50
});Bulk Import
const result = await metigan.contacts.bulkImport(
[
{ email: '[email protected]', firstName: 'John' },
{ email: '[email protected]', firstName: 'Jane' },
{ email: '[email protected]', firstName: 'Peter', tags: ['vip'] }
],
'audience-123'
);
console.log(`Imported: ${result.imported}`);
console.log(`Failed: ${result.failed}`);Search Contacts
const results = await metigan.contacts.search('doe', 'audience-123');📊 Audiences Module
Create Audience
const audience = await metigan.audiences.create({
name: 'Main Newsletter',
description: 'Main subscriber list'
});List Audiences
const { audiences, pagination } = await metigan.audiences.list({
page: 1,
limit: 10
});
audiences.forEach(audience => {
console.log(`${audience.name}: ${audience.count} contacts`);
});Audience Statistics
const stats = await metigan.audiences.getStats('audience-123');
console.log(`Total: ${stats.total}`);
console.log(`Subscribed: ${stats.subscribed}`);
console.log(`Unsubscribed: ${stats.unsubscribed}`);
console.log(`Bounced: ${stats.bounced}`);Clean Audience
// Remove bounced and unsubscribed contacts
const { removed } = await metigan.audiences.clean('audience-123');
console.log(`${removed} contacts removed`);Merge Audiences
// Merge source into target (source is deleted)
const merged = await metigan.audiences.merge(
'source-audience-id',
'target-audience-id'
);⚙️ Advanced Configuration
const metigan = new Metigan({
apiKey: 'your-api-key',
// User ID for logging
userId: 'user-123',
// Disable logging
disableLogs: true,
// Timeout in milliseconds
timeout: 60000,
// Number of retries on failure
retryCount: 5,
// Delay between retries (ms)
retryDelay: 2000,
// Security options (see Security section)
debug: false,
sanitizeHtml: true,
enableRateLimit: true,
maxRequestsPerSecond: 10
});🔒 Security Features
The SDK includes built-in security features:
HTML Sanitization
Automatically removes dangerous HTML tags and attributes from email content to prevent XSS attacks:
// HTML sanitization is enabled by default
const metigan = new Metigan({
apiKey: 'your-api-key',
sanitizeHtml: true // default
});
// Dangerous content is automatically sanitized
await metigan.email.sendEmail({
from: '[email protected]',
recipients: ['[email protected]'],
subject: 'Newsletter',
content: '<h1>Hello</h1><script>alert("xss")</script>' // Script tag will be removed
});Client-Side Rate Limiting
Prevents accidental API abuse with built-in rate limiting:
const metigan = new Metigan({
apiKey: 'your-api-key',
enableRateLimit: true, // default
maxRequestsPerSecond: 10 // default
});
// Check rate limit before making requests
if (metigan.email.canMakeRequest()) {
await metigan.email.sendEmail({...});
} else {
const waitTime = metigan.email.getTimeUntilNextRequest();
console.log(`Please wait ${waitTime}ms before next request`);
}
// Reset rate limiter if needed
metigan.email.resetRateLimit();Attachment Validation
Validates file attachments for security:
import { isAllowedMimeType, isSafeFileExtension, ALLOWED_MIME_TYPES } from 'metigan';
// Check if file type is safe
const filename = 'document.pdf';
const mimetype = 'application/pdf';
if (isSafeFileExtension(filename) && isAllowedMimeType(mimetype)) {
// Safe to attach
}
// View allowed MIME types
console.log(ALLOWED_MIME_TYPES);Email Header Injection Prevention
Email addresses and subjects are automatically sanitized to prevent header injection attacks:
import { sanitizeEmail, sanitizeSubject } from 'metigan';
// Remove newlines and dangerous characters
const safeEmail = sanitizeEmail('[email protected]\r\nBcc: [email protected]');
const safeSubject = sanitizeSubject('Subject\r\nFrom: [email protected]');Debug Mode
Enable debug mode for troubleshooting (logs are hidden by default in production):
const metigan = new Metigan({
apiKey: 'your-api-key',
debug: true // Shows internal logs
});
// Or enable/disable at runtime
metigan.email.enableDebug();
metigan.email.disableDebug();Custom Rate Limiter
For advanced use cases, you can create your own rate limiter:
import { RateLimiter } from 'metigan';
const limiter = new RateLimiter({
maxRequests: 5,
windowMs: 1000 // 5 requests per second
});
if (limiter.tryRequest()) {
// Request allowed and recorded
await makeApiCall();
} else {
// Rate limited
console.log(`Wait ${limiter.getTimeUntilNextRequest()}ms`);
}🔧 Using Individual Modules
If you only need a specific module:
import { MetiganForms, MetiganContacts, MetiganAudiences } from 'metigan';
// Forms only
const forms = new MetiganForms({
apiKey: 'your-api-key'
});
// Contacts only
const contacts = new MetiganContacts({
apiKey: 'your-api-key'
});
// Audiences only
const audiences = new MetiganAudiences({
apiKey: 'your-api-key'
});🌐 Browser Usage
<script src="https://unpkg.com/metigan/dist/index.js"></script>
<script>
const metigan = new Metigan({ apiKey: 'your-api-key' });
// Submit form
document.getElementById('my-form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
try {
const response = await metigan.forms.submit({
formId: 'form-123',
data
});
alert(response.message);
} catch (error) {
alert('Error submitting: ' + error.message);
}
});
</script>🛡️ Error Handling
import { MetiganError, ValidationError, ApiError } from 'metigan';
try {
await metigan.email.sendEmail({
from: 'invalid',
recipients: [],
subject: '',
content: ''
});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid data:', error.message);
} else if (error instanceof ApiError) {
console.error(`API error (${error.status}):`, error.message);
} else if (error instanceof MetiganError) {
console.error('Metigan error:', error.message);
} else {
console.error('Unknown error:', error);
}
}📝 TypeScript
The library includes full TypeScript definitions:
import Metigan, {
EmailOptions,
OtpSendOptions,
TransactionalSendOptions,
FormConfig,
Contact,
Audience,
FormFieldType
} from 'metigan';
const emailOptions: EmailOptions = {
from: '[email protected]',
recipients: ['[email protected]'],
subject: 'Test',
content: '<p>Content</p>'
};
const fieldType: FormFieldType = 'email';
const otpOptions: OtpSendOptions = {
from: '[email protected]',
to: '[email protected]',
code: '123456'
};
const transactionalOptions: TransactionalSendOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Invoice ready',
content: '<p>Your invoice is ready.</p>'
};📄 License
MIT © Metigan
