# FormaMail Complete Documentation > This file contains comprehensive FormaMail documentation for LLM consumption. > FormaMail is a transactional document infrastructure platform. > Generated: 2025-12-12T09:13:26.870Z --- ## Pricing FormaMail uses a simple block-based pricing model: - **1 Block = 10,000 emails** - **Signup Bonus**: 1,000 free emails (no credit card required) - **Credits Never Expire**: For active accounts (see Account Dormancy Policy) ### Volume Discounts | Blocks | Tier | Price/Block | Price/Email | |--------|------|-------------|-------------| | 1-5 | Starter | $30.00 | $0.003000 | | 6-10 | Growth | $24.00 | $0.002400 | | 11-50 | Business | $21.00 | $0.002100 | | 51-100 | Scale | $18.00 | $0.001800 | | 101+ | Enterprise | $15.00 | $0.001500 | ### Key Benefits - No monthly subscription - pay only for what you use - Volume discounts applied automatically - Credits never expire for active accounts - Full API access on all tiers - Unlimited templates ### Account Dormancy Policy - **Active accounts**: Credits never expire as long as you remain active - **Definition of active**: Any dashboard login, API call, or integration usage within the past 12 months - **Dormant accounts**: After 12 months of inactivity, credits are reset to zero - **What's preserved**: Templates, account settings, and API keys (disabled until reactivation) - **Reactivation**: Simply log back in - your templates are waiting, just purchase new credits to resume sending --- ## Table of Contents 1. Getting Started 2. Sending Emails with Attachments 3. Replace SendGrid + DocRaptor with One API Call 4. Developer Guide 5. Template Schema Reference 6. Recipes 7. B2B Invoice Email 8. Order Confirmation + Receipt 9. Weekly Excel Report 10. Integrations 11. No-Code Integrations 12. Webhook Subscriptions 13. Zapier Starter Packs 14. FormaMail Documentation 15. Limits & Quotas 16. Compliance & Certifications 17. Data Handling & Security 18. Security & Compliance 19. Infrastructure & Reliability 20. Edit Templates with AI 21. Generate Templates with AI 22. Tutorials 23. Feedback Widget 24. User Guide 25. Frequently Asked Questions --- # Getting Started Source: /getting-started # Getting Started Welcome to FormaMail! This guide will help you get up and running in just a few minutes. ## What You'll Learn - How to create an account and set up your team - How to generate your first API key - How to send your first email - Next steps after getting started --- ## Step 1: Create an Account 1. Visit [https://app.formamail.com/signup](https://app.formamail.com/signup) 2. Enter your email address and create a strong password 3. Click **Create Account** 4. Check your email for the verification link 5. Click the verification link to activate your account --- ## Step 2: Complete Your Profile After creating your account, you'll be guided through a quick onboarding: 1. **Company Information**: Enter your company name and role 2. **Team Size**: Select your team size 3. **Use Case**: Tell us how you plan to use FormaMail This helps us customize your experience! --- ## Step 3: Create Your First API Key API keys are required to send emails programmatically. ### Via Dashboard: 1. Navigate to **Settings** → **API Keys** 2. Click **Create New API Key** 3. Give it a descriptive name (e.g., "Production Key") 4. Select the permissions you need: - ✅ Send Emails - ✅ Manage Templates - ✅ View Analytics 5. Click **Create Key** ### Example API Key: ``` fm_sk_abc123xyz456... ``` Store this securely in your environment variables: ```bash # .env FORMAMAIL_API_KEY=fm_sk_abc123xyz456... ``` --- ## Step 4: Send Your First Email Now let's send your first email using the API! ### Quick Test with API Playground Use our API Playground for the easiest way to test: 1. Go to **API** → **Playground** 2. Select the **Send Email** endpoint 3. Choose a template from the dropdown 4. Fill in the recipient email and variables 5. Click **Send Request** You should receive the email within seconds! ### Using cURL ```bash curl -X POST https://api.formamail.com/api/emails/send \ -H "Authorization: Bearer fm_sk_abc123xyz456..." \ -H "Content-Type: application/json" \ -d '{ "templateId": "your-template-id", "to": [ { "email": "recipient@example.com", "name": "John Doe" } ], "variables": { "firstName": "John", "message": "Hello from FormaMail!" } }' ``` ### Using JavaScript/TypeScript ```typescript async function sendEmail() { const response = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': 'Bearer fm_sk_abc123xyz456...', 'Content-Type': 'application/json', }, body: JSON.stringify({ templateId: 'your-template-id', to: [ { email: 'recipient@example.com', name: 'John Doe' } ], variables: { firstName: 'John', message: 'Hello from FormaMail!', }, }), }) const data = await response.json() console.log('Email sent:', data) } sendEmail() ``` --- ## Step 5: Create Your First Template Templates allow you to create reusable email designs: 1. Navigate to **Templates** → **Create New Template** 2. Choose **Email Template** 3. Use the drag-and-drop designer to create your layout 4. Add variables like `{{name}}` and `{{companyName}}` 5. Click **Save** and **Publish** Now you can use this template when sending emails! --- ## Next Steps Now that you've sent your first email, here's what to explore next: ### 🎨 Design Templates Learn how to create beautiful email templates with our visual designer. 👉 [Template Designer Guide](/user-guide/email-designer) ### 📊 Track Performance Monitor email opens, clicks, and delivery status. 👉 [Analytics Guide](/user-guide/analytics) ### 🔌 API Integration Integrate FormaMail into your application. 👉 [API Reference](/api-reference) ### 📎 Generate Attachments Create PDF invoices and Excel reports from templates. 👉 [Attachment Guide](/user-guide/attachments) --- ## Common Questions ### How many emails can I send? You get **1,000 free emails** when you sign up (no credit card required). After that, you can purchase email credits in blocks - 1 block = 10,000 emails. Credits never expire! Check our [Pricing Page](https://formamail.com/pricing) for volume discounts. ### Can I use my own domain? Yes! You can configure a custom sending domain in **Settings** → **Domains**. ### Where can I view sent emails? All sent emails are visible in **Email Logs** with detailed status information. ### Do you support attachments? Yes! You can attach files or generate PDFs/Excel files from templates. --- ## Need Help? - 📖 [User Guide](/user-guide) - 💻 [Developer Guide](/developer-guide) - 📚 [API Reference](/api-reference) - 💬 [Discord Community](https://discord.gg/formamail) - 📧 support@formamail.com --- **Ready to dive deeper?** → [User Guide](/user-guide) --- # Sending Emails with Attachments Source: /developer-guide/sending-with-attachments # Sending Emails with Attachments This is FormaMail's superpower: send transactional emails with dynamically generated PDF or Excel attachments in a single API call. No Puppeteer. No S3. No glue code. ## Quick Example ```javascript // Send order confirmation with PDF invoice attached const response = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "order-confirmation", to: [{ email: "customer@example.com", name: "John Doe" }], variables: { orderId: "12345", customerName: "John Doe", items: [ { name: "Widget", qty: 2, price: 29.99 }, { name: "Gadget", qty: 1, price: 49.99 } ], total: 109.97 }, attachments: [{ attachmentTemplateId: "invoice-pdf" }] }) }); const result = await response.json(); console.log('Email sent:', result.emailId); // Customer receives email with professionally formatted PDF invoice attached ``` **Result**: Customer receives an email with a dynamically generated PDF invoice attached, using the same order data for both email and PDF. ## How It Works 1. **You call the API** with email template ID + attachment template ID + variables 2. **FormaMail renders the email** using your email variables 3. **FormaMail generates the PDF/Excel** using attachment variables (provided separately or inherited) 4. **FormaMail attaches the file** to the email 5. **FormaMail sends** the email via AWS SES 6. **You receive a response** with the email ID All in one API call. No intermediate services. No temporary storage. ## Providing Variables FormaMail supports **variable inheritance** to reduce payload size and simplify API calls: - **Email variables** are provided in the `variables` field - **Attachment variables** inherit from email variables automatically - **Attachment-specific variables** (in `attachments[].variables`) can override or extend inherited values ### Variable Priority (lowest to highest) 1. **Email-level variables** - Base variables from `variables` field 2. **Attachment-specific variables** - Override/extend from `attachments[].variables` Your email template might have variables like: ```handlebars Hi {{customerName}}, Your order #{{orderId}} is confirmed. Total: ${{total}} See the attached invoice for details. ``` Your PDF invoice template has its own variables: ```handlebars INVOICE #{{orderId}} Bill To: {{customerName}} {{#each items}} {{name}} - Qty: {{qty}} - ${{price}} {{/each}} Total: ${{total}} ``` **Define your data once. Use it everywhere.** ## Attachment Options ### 1. Generate from Template (Recommended) The most common approach - generate PDF/Excel from a FormaMail template: ```json { "templateId": "order-email", "to": [{"email": "customer@example.com"}], "variables": { "orderId": "12345" }, "attachments": [{ "attachmentTemplateId": "invoice-template", "filename": "invoice-12345.pdf", "outputFormats": ["pdf"] }] } ``` | Field | Required | Description | |-------|----------|-------------| | `attachmentTemplateId` | Yes | Template ID, shortId, or slug | | `filename` | No | Custom filename (default: template name) | | `outputFormats` | No | Array: `["pdf"]`, `["excel"]`, or `["pdf", "excel"]` | | `variables` | No | Override variables (merged with email variables) | ### 2. Multiple Attachments Attach multiple files to a single email: ```json { "attachments": [ { "attachmentTemplateId": "invoice-pdf", "filename": "invoice.pdf" }, { "attachmentTemplateId": "receipt-excel", "outputFormats": ["excel"], "filename": "receipt.xlsx" } ] } ``` ### 3. Both PDF and Excel from Same Template Generate both formats from one template: ```json { "attachments": [{ "attachmentTemplateId": "monthly-report", "outputFormats": ["pdf", "excel"], "filename": "report-november" }] } ``` Results in two attachments: - `report-november.pdf` - `report-november.xlsx` ### 4. Variable Inheritance (Recommended) Attachments automatically inherit email variables. Only specify attachment-specific overrides: ```json { "templateId": "shipping-notification", "variables": { "customerName": "John", "trackingNumber": "1Z999AA10123456784", "orderItems": [...] }, "attachments": [{ "attachmentTemplateId": "packing-slip", "variables": { "includeBarcode": true, "warehouseNotes": "Fragile - Handle with care" } }] } ``` **What the attachment receives:** ```javascript { // Inherited from email variables customerName: "John", trackingNumber: "1Z999AA10123456784", orderItems: [...], // Plus attachment-specific variables includeBarcode: true, warehouseNotes: "Fragile - Handle with care" } ``` ### 5. Attach Static Files (Base64) Attach a file you already have: ```json { "attachments": [{ "filename": "contract.pdf", "content": "JVBERi0xLjQK...", // Base64 encoded "contentType": "application/pdf" }] } ``` ### 6. Attach from URL Attach a file from a URL: ```json { "attachments": [{ "filename": "report.pdf", "url": "https://example.com/reports/monthly.pdf" }] } ``` ### 7. Attach from Asset Gallery Attach a file from your uploaded assets: ```json { "attachments": [{ "assetId": "asset-uuid-here" }] } ``` ## Template Identification You can reference templates by UUID, shortId, or slug: ```json // UUID "attachmentTemplateId": "550e8400-e29b-41d4-a716-446655440000" // Short ID (prefixed) "attachmentTemplateId": "atpl_a1b2c3d4" // Slug (human-readable) "attachmentTemplateId": "monthly-invoice" ``` ## Output Formats ### PDF Best for: - Invoices and receipts - Reports and statements - Contracts and agreements - Any document intended for print ```json "outputFormats": ["pdf"] ``` ### Excel Best for: - Data exports - Spreadsheets with formulas - Reports users will edit - Data for further analysis ```json "outputFormats": ["excel"] ``` ### Both Generate both formats: ```json "outputFormats": ["pdf", "excel"] ``` ## Full API Schema ```typescript interface SendEmailRequest { // Email template templateId: string; // Recipients (up to 50) to: Array<{ email: string; name?: string; }>; // Optional CC/BCC cc?: Array<{ email: string; name?: string }>; bcc?: Array<{ email: string; name?: string }>; // Template variables variables: Record; // Attachments (up to 10) attachments?: Array< | TemplateAttachment | Base64Attachment | UrlAttachment | AssetAttachment >; // Optional overrides subject?: string; // Override template subject replyTo?: string; // Override reply-to address metadata?: Record; // Custom tracking data } interface TemplateAttachment { attachmentTemplateId: string; filename?: string; outputFormats?: Array<'pdf' | 'excel'>; variables?: Record; } interface Base64Attachment { filename: string; content: string; // Base64 encoded contentType: string; } interface UrlAttachment { filename: string; url: string; } interface AssetAttachment { assetId: string; } ``` ## Code Examples ### Node.js / Express ```javascript const express = require('express'); const app = express(); app.post('/send-invoice', async (req, res) => { const { order, customer } = req.body; try { const response = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: 'order-confirmation', to: [{ email: customer.email, name: customer.name }], variables: { orderId: order.id, orderDate: new Date().toLocaleDateString(), customerName: customer.name, items: order.items, subtotal: order.subtotal, tax: order.tax, total: order.total }, attachments: [{ attachmentTemplateId: 'invoice-pdf', filename: `invoice-${order.id}.pdf` }] }) }); const result = await response.json(); if (!response.ok) { throw new Error(result.message); } res.json({ success: true, emailId: result.emailId }); } catch (error) { res.status(500).json({ error: error.message }); } }); ``` ### Python ```python def send_invoice_email(order, customer): response = requests.post( 'https://api.formamail.com/api/emails/send', headers={ 'Authorization': f"Bearer {os.environ['FORMAMAIL_API_KEY']}", 'Content-Type': 'application/json' }, json={ 'templateId': 'order-confirmation', 'to': [{'email': customer['email'], 'name': customer['name']}], 'variables': { 'orderId': order['id'], 'customerName': customer['name'], 'items': order['items'], 'total': order['total'] }, 'attachments': [{ 'attachmentTemplateId': 'invoice-pdf', 'filename': f"invoice-{order['id']}.pdf" }] } ) response.raise_for_status() return response.json() ``` ### cURL ```bash curl -X POST https://api.formamail.com/api/emails/send \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{ "templateId": "order-confirmation", "to": [{"email": "customer@example.com", "name": "John Doe"}], "variables": { "orderId": "12345", "customerName": "John Doe", "items": [{"name": "Widget", "qty": 2, "price": 29.99}], "total": 59.98 }, "attachments": [{ "attachmentTemplateId": "invoice-pdf", "filename": "invoice-12345.pdf" }] }' ``` ## Limits | Limit | Value | |-------|-------| | Attachments per email | 10 | | Total attachment size | 25 MB | | Single attachment | 10 MB | | PDF pages | 50 | | Excel rows per sheet | 100,000 | See [Limits & Quotas](/limits) for the complete list. ## Common Patterns ### Invoice on Order Completion ```javascript // In your order completion handler async function onOrderComplete(order) { await sendEmail({ templateId: 'order-confirmation', to: [{ email: order.customerEmail }], variables: formatOrderVariables(order), attachments: [{ attachmentTemplateId: 'invoice-pdf', filename: `invoice-${order.id}.pdf` }] }); } ``` ### Weekly Report with Excel ```javascript // Scheduled job (cron) async function sendWeeklyReport() { const data = await getWeeklyAnalytics(); await sendEmail({ templateId: 'weekly-report', to: [ { email: 'team@company.com' }, { email: 'leadership@company.com' } ], variables: { weekOf: getWeekRange(), metrics: data.summary, dailyBreakdown: data.daily }, attachments: [{ attachmentTemplateId: 'analytics-report', outputFormats: ['pdf', 'excel'], filename: `report-week-${getWeekNumber()}` }] }); } ``` ### Contract with Signature ```javascript // Send contract with company signature already embedded await sendEmail({ templateId: 'contract-email', to: [{ email: client.email }], variables: { clientName: client.name, contractTerms: contractDetails, signatureDate: new Date().toLocaleDateString() }, attachments: [{ attachmentTemplateId: 'service-agreement', filename: `contract-${client.id}.pdf` }] }); ``` ## Error Handling ```javascript const response = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }); if (!response.ok) { const error = await response.json(); switch (error.code) { case 'ERR_TMPL_002': console.error('Template not found:', error.relatedInfo.templateId); break; case 'ERR_ATTACH_001': console.error('Attachment template not found'); break; case 'ERR_ATTACH_002': console.error('Attachment too large'); break; case 'ERR_QUOTA_001': console.error('Rate limit exceeded, retry after:', error.retryAfter); break; default: console.error('Unknown error:', error); } } ``` ## FAQ ### How long does attachment generation take? - PDF: 2-10 seconds depending on complexity - Excel: 1-5 seconds depending on data size The API responds immediately with a queued status. Actual delivery happens within seconds to minutes depending on queue depth. ### Can I track if the attachment was opened? FormaMail tracks email opens and link clicks, but cannot track if a PDF attachment was opened (this would require desktop software). ### What happens if attachment generation fails? The email will not be sent. You'll receive an error response with details about the failure. Common causes: - Invalid template variables - Template references non-existent images - Data exceeds limits (too many pages, rows, etc.) ### Can I preview the attachment before sending? Yes! Use the attachment preview endpoint: ```bash GET /api/templates/attachment/:id/preview?variables={...} ``` Or use the preview feature in the dashboard template editor. ## Next Steps - [Create attachment templates](/user-guide/attachment-templates) - [Learn about template variables](/developer-guide/templates) - [Explore PDF generation options](/developer-guide/attachments) - [See complete recipes](/recipes) --- # Replace SendGrid + DocRaptor with One API Call Source: /recipes/replace-sendgrid # Replace SendGrid + DocRaptor with One API Call ## The Problem You're currently using multiple services to send emails with PDF attachments: - **SendGrid** (or Mailgun/Postmark) for sending emails - **DocRaptor** (or Puppeteer/wkhtmltopdf) for PDF generation - **S3** (or filesystem) for temporary PDF storage - **Glue code** to stitch it all together That's 3-4 services, 3-4 API keys, and 25+ lines of code for every invoice email. ## The Solution FormaMail combines email sending AND document generation into a single API call. Create email and PDF templates, then send emails with dynamically generated attachments in one request. ## Before vs After ### Before: SendGrid + Puppeteer + S3 **Problems with this approach:** - 25+ lines of code - 3 services to maintain (SendGrid, Puppeteer, S3) - PDF server resources (memory, CPU, browser instances) - S3 cleanup often forgotten (storage costs add up) - Multiple failure points - Rate limiting across multiple services ### After: FormaMail **Benefits:** - ~15 lines of code (80% reduction) - 1 API key - No PDF server to manage - No S3 bucket needed - Single failure point with automatic retries - One bill, one dashboard ## Step-by-Step Migration ### Update Your Code Replace your SendGrid + Puppeteer code with the FormaMail API call shown above. **Key mappings:** - `sgMail.send({ to: ... })` → `to: [{ email: ... }]` - `renderEmailHTML(data)` → `templateId` + `variables` - `renderInvoiceHTML(data) + Puppeteer` → `attachments[0].attachmentTemplateId` ### Test in API Playground 1. Go to the [API Playground](/api-reference/explorer) in the docs 2. Enter your API key 3. Test the `/api/emails/send` endpoint with your template IDs 4. Verify the email and PDF look correct ### Deploy to Production ```bash # Remove old dependencies npm uninstall @sendgrid/mail puppeteer aws-sdk # Or for Python pip uninstall sendgrid weasyprint boto3 # FormaMail doesn't require an SDK - just HTTP requests! ``` ### Clean Up Old Services - Cancel SendGrid subscription - Shut down PDF generation server (if using Puppeteer) - Delete S3 bucket or cleanup Lambda - Remove old API keys from environment variables ## Migration Checklist - [ ] Create email template in FormaMail dashboard - [ ] Create PDF attachment template - [ ] Map your existing variables to FormaMail template variables - [ ] Test with FormaMail API Playground - [ ] Replace code in staging environment - [ ] Verify email delivery and PDF quality - [ ] Deploy to production - [ ] Monitor for issues (check email logs in dashboard) - [ ] Remove old service dependencies - [ ] Cancel old service accounts ## Cost Comparison | Service | Monthly Cost (10K emails/PDFs) | Notes | |---------|-------------------------------|-------| | SendGrid Pro | ~$20/month | Email only | | DocRaptor | ~$30/month | 10K docs | | S3 Storage | ~$5/month | Storage + transfer | | **Old Total** | **~$55/month** | + infrastructure costs | | **FormaMail** | **~$40/month** | All-inclusive | ## Common Migration Questions ### What about my existing templates? You'll need to recreate templates in FormaMail's designer, but this is often an improvement. The visual designer makes templates easier to maintain than code-based templates. ### Do I need to change my variable structure? FormaMail supports nested objects and arrays, just like Handlebars or EJS. Your existing data structures will likely work with minimal changes. ```javascript // Your existing data structure probably works as-is variables: { customer: { name: "John Doe", email: "john@example.com" }, items: [ { name: "Widget", qty: 2, price: 29.99 }, { name: "Gadget", qty: 1, price: 49.99 } ] } // Use in templates as: // {{customer.name}} // {{#each items}}{{name}} - {{qty}} x ${{price}}{{/each}} ``` ### What about email tracking? FormaMail includes built-in tracking for: - Email opens - Link clicks - Delivery status - Bounces and complaints No additional setup required. ### Can I send without PDF attachment? Yes! The `attachments` array is optional. Remove it to send email-only: ```javascript { templateId: "welcome-email", to: [{ email: "user@example.com" }], variables: { name: "John" } // No attachments = email only } ``` ## Next Steps - [Create your first template](/user-guide/templates) - [Explore the API reference](/api-reference) - [Learn about attachment options](/developer-guide/sending-with-attachments) --- # Developer Guide Source: /developer-guide # Developer Guide Welcome to the FormaMail Developer Guide! This guide provides comprehensive technical documentation for integrating FormaMail into your applications. ## What You'll Learn This developer guide covers everything you need to integrate FormaMail programmatically: ### Getting Started - **[Authentication](/developer-guide/authentication)** - API keys, JWT tokens, and OAuth - **[Sending Emails](/developer-guide/sending-emails)** - Send transactional and bulk emails - **[Working with Templates](/developer-guide/templates)** - Create, manage, and use templates via API ### Advanced Features - **[Generating Attachments](/developer-guide/attachments)** - Create PDF invoices and Excel reports - **[Error Handling](/developer-guide/error-handling)** - Handle API errors gracefully - **[Rate Limiting](/developer-guide/rate-limiting)** - Understand API limits and quotas ### Integration - **[Best Practices](/developer-guide/best-practices)** - Security, performance, and optimization tips --- ## Quick Example Here's a quick example to get you started with sending an email: ```javascript const response = await fetch('http://localhost:3000/api/emails/send', { method: 'POST', headers: { 'Authorization': 'Bearer fm_sk_abc123xyz456...', 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: 'tmpl_welcome', to: [ { email: 'recipient@example.com', name: 'John Doe' } ], variables: { firstName: 'John', companyName: 'Acme Corp' } }) }); const data = await response.json(); console.log('Email sent:', data.id); ``` **Note**: All emails must use a template. Templates are created via the [Dashboard](/user-guide/templates). --- ## Base URL All API requests should be made to: ``` https://api.formamail.com ``` --- ## Authentication FormaMail uses API keys for authentication. Include your API key in the `Authorization` header: ```bash curl https://api.formamail.com/api/emails/send \ -H "Authorization: Bearer fm_sk_abc123xyz456..." \ -H "Content-Type: application/json" ``` Learn more about [Authentication →](/developer-guide/authentication) --- ## Language Examples The FormaMail API is a standard REST API that works with any HTTP client. Here are examples in popular languages: - **Node.js** - Using fetch or axios - **Python** - Using requests library - **PHP** - Using cURL or Guzzle - **Ruby** - Using HTTParty - **Go** - Using net/http See specific endpoint documentation in the [API Reference](/api-reference) for detailed examples. --- ## Getting Help - 📚 Check the [API Reference](/api-reference) for complete endpoint documentation - 📖 Browse the [User Guide](/user-guide) for platform features - 💬 Join our [Discord Community](https://discord.gg/formamail) - 📧 Email us: developers@formamail.com --- **Ready to integrate?** → Start with [Authentication](/developer-guide/authentication) --- # Template Schema Reference Source: /developer-guide/template-schema # Template Schema Reference This guide defines the exact JSON schema structure for email and attachment templates in FormaMail. Use this as the authoritative reference when creating templates programmatically, via API, or with AI assistants. ## AI/LLM Import Format When generating templates with AI, you need to provide two separate JSON structures: 1. **Variables Array** - The data schema defining what variables the template expects 2. **Template Schema** - The visual design structure (sections and components) ### Variables Format **⚠️ CRITICAL FOR AI/LLM**: Variables must be a **PLAIN JSON ARRAY** `[...]`, NOT wrapped in an object like `{ "variables": [...] }`. **✅ CORRECT format (plain array):** ```json [ { "id": "var_1", "name": "customerName", "type": "string", "required": true, "description": "Customer's full name", "defaultValue": "John Doe" }, { "id": "var_2", "name": "orderTotal", "type": "number", "required": true, "defaultValue": 99.99 } ] ``` **❌ INCORRECT format (DO NOT USE):** ```json { "variables": [ { "name": "customerName", "type": "string" } ] } ``` This WILL NOT work. Variables must be a plain array, not wrapped in an object. ### Variable Object Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | No* | Unique identifier (auto-generated if omitted) | | `name` | string | **Yes** | Variable name (used in `{{variableName}}` syntax) | | `type` | string | **Yes** | One of: `string`, `number`, `boolean`, `date`, `array`, `object` | | `required` | boolean | No | Whether the variable is required (default: `false`) | | `description` | string | No | Human-readable description | | `defaultValue` | any | **Recommended** | Default/sample value for testing and preview | | `isCalculated` | boolean | No | `true` if this is a calculated variable | | `expression` | string | No | JavaScript expression (required if `isCalculated: true`) | | `schema` | object | No | Type schema for `array` and `object` types | | `format` | object | No | Display format configuration | *If `id` is omitted, it will be auto-generated when imported. ### Array Variable Example ```json { "id": "var_items", "name": "items", "type": "array", "itemType": "object", "required": true, "description": "List of order items", "defaultValue": [ { "name": "Product A", "quantity": 1, "price": 29.99 } ], "schema": { "type": "array", "itemType": "object", "itemSchema": { "properties": { "name": { "type": "string" }, "quantity": { "type": "number" }, "price": { "type": "number" } } } } } ``` ### Object Variable Example ```json { "id": "var_customer", "name": "customer", "type": "object", "required": true, "description": "Customer information", "defaultValue": { "name": "Jane Smith", "email": "jane@example.com" }, "schema": { "type": "object", "properties": { "name": { "type": "string", "description": "Customer name" }, "email": { "type": "string", "description": "Customer email" } } } } ``` ## Email Template Schema Email templates use a `sections` array at the root level. ```json { "sections": [ { "id": "body-section", "type": "body", "props": { "backgroundColor": "#ffffff", "padding": "40px 20px" }, "components": [ // Array of component objects ] } ] } ``` ### Section Types | Type | Description | |------|-------------| | `header` | Top section (logo, navigation) | | `main` or `body` | Main content section | | `footer` | Bottom section (copyright, links) | ## Attachment Template Schema PDF and Excel templates use a `pages` array with nested sections: ```json { "outputFormats": ["pdf"], "pages": [ { "pageId": "page-1", "pageName": "Invoice", "pageSettings": { "format": "A4", "orientation": "portrait", "margin": { "top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm" } }, "sections": [ { "id": "main-section", "type": "main", "props": {}, "components": [] } ] } ] } ``` ### Output Formats | Format | Description | |--------|-------------| | `["pdf"]` | Generate PDF attachment | | `["excel"]` | Generate Excel attachment | | `["pdf", "excel"]` | Generate both formats | ### Page Settings | Field | Type | Default | Description | |-------|------|---------|-------------| | `format` | string | `"A4"` | `"A4"`, `"Letter"`, `"Legal"`, `"A3"`, `"A5"`, `"Tabloid"` | | `orientation` | string | `"portrait"` | `"portrait"` or `"landscape"` | | `margin` | object | - | Page margins (top, right, bottom, left) | | `backgroundColor` | string | `"#ffffff"` | Page background color | | `header` | object | - | Header configuration | | `footer` | object | - | Footer configuration | ## Component Reference ### Component Types by Template Type | Component | Email | PDF | Excel | Description | |-----------|:-----:|:---:|:-----:|-------------| | `rich-text` | ✓ | ✓ | ✓ | Rich text with HTML | | `button` | ✓ | ✗ | ✗ | CTA buttons | | `image` | ✓ | ✓ | ✓ | Images | | `table` | ✓ | ✓ | ✓ | Data tables | | `container` | ✓ | ✓ | ✗ | Component wrapper | | `columns` | ✓ | ✓ | ✗ | Multi-column layout | | `divider` | ✓ | ✓ | ✗ | Horizontal rule | | `spacer` | ✓ | ✓ | ✗ | Vertical space | | `conditional` | ✓ | ✓ | ✓ | If/else rendering | | `loop` | ✓ | ✓ | ✓ | Iteration over arrays | | `list` | ✓ | ✓ | ✗ | Ordered/unordered lists | | `qr-code` | ✓ | ✓ | ✗ | QR codes | | `html-fragment` | ✓ | ✗ | ✗ | Raw HTML (email only) | | `chart` | ✗ | ✓ | ✓ | Charts (attachments only) | | `timeline` | ✓ | ✓ | ✗ | Activity timeline | ### Component Structure Components use one of three patterns: **1. Content-based** (rich-text, button, list): ```json { "id": "welcome-text", "type": "rich-text", "props": { "marginBottom": "16px" }, "content": "

Hello {{name}}

" } ``` **2. Props-based** (image, divider, spacer, qr-code): ```json { "id": "logo", "type": "image", "props": { "src": "{{company.logo}}", "alt": "Logo", "width": "120px" } } ``` **3. Children-based** (container, columns, conditional, loop): ```json { "id": "wrapper", "type": "container", "props": { "padding": "20px" }, "children": [ { "id": "child-1", "type": "rich-text", "content": "..." } ] } ``` ## Variable Syntax ### Basic Substitution ``` {{variableName}} {{customer.firstName}} {{order.items[0].name}} ``` ### In Content ```json { "type": "rich-text", "content": "

Hello {{customer.name}}, your order #{{order.number}} is ready.

" } ``` ### In Props ```json { "type": "button", "props": { "href": "{{trackingUrl}}" }, "content": "Track Package" } ``` ## Conditional Logic ```json { "id": "discount-banner", "type": "conditional", "props": { "condition": "hasDiscount", "operator": "==", "value": true }, "children": [ { "id": "discount-text", "type": "rich-text", "content": "

You saved ${{discountAmount}}!

" } ] } ``` ### Operators | Operator | Description | |----------|-------------| | `==` | Equals | | `!=` | Not equals | | `>` | Greater than | | `<` | Less than | | `>=` | Greater than or equal | | `<=` | Less than or equal | | `contains` | String/array contains | | `startsWith` | String starts with | | `endsWith` | String ends with | | `exists` | Value exists (not null/undefined) | | `isEmpty` | Value is empty | ## Loop Iteration ```json { "id": "items-loop", "type": "loop", "props": { "dataSource": "{{items}}", "itemVariable": "item", "indexVariable": "index" }, "children": [ { "id": "item-row", "type": "rich-text", "content": "

{{index}}. {{item.name}} - ${{item.price}}

" } ] } ``` ### Loop Props | Prop | Required | Description | |------|----------|-------------| | `dataSource` | Yes | Variable containing array: `{{items}}` | | `itemVariable` | Yes | Name for current item: `item` | | `indexVariable` | No | Name for current index: `index` | ## System-Provided Global Constants These variables are automatically available in all templates: ### Date Constants | Variable | Example Value | |----------|---------------| | `{{__CURRENT_DATE__}}` | `2025-12-02` | | `{{__CURRENT_YEAR__}}` | `2025` | | `{{__CURRENT_MONTH__}}` | `December` | | `{{__CURRENT_DAY__}}` | `2` | | `{{__CURRENT_DAY_NAME__}}` | `Tuesday` | ### Company Constants | Variable | Description | |----------|-------------| | `{{__COMPANY_NAME__}}` | Company display name | | `{{__COMPANY_LEGAL_NAME__}}` | Legal name | | `{{__COMPANY_EMAIL__}}` | Contact email | | `{{__COMPANY_PHONE__}}` | Phone number | | `{{__COMPANY_WEBSITE__}}` | Website URL | | `{{__COMPANY_ADDRESS_LINE1__}}` | Address line 1 | | `{{__COMPANY_CITY__}}` | City | | `{{__COMPANY_STATE__}}` | State/Province | | `{{__COMPANY_POSTAL_CODE__}}` | Postal/ZIP code | | `{{__COMPANY_COUNTRY__}}` | Country | ## Complete Example ### Variables ```json [ { "id": "var_customer", "name": "customer", "type": "object", "required": true, "defaultValue": { "name": "Jane Smith", "email": "jane@example.com" }, "schema": { "type": "object", "properties": { "name": { "type": "string" }, "email": { "type": "string" } } } }, { "id": "var_items", "name": "items", "type": "array", "itemType": "object", "required": true, "defaultValue": [ { "name": "Widget", "qty": 2, "price": 25.00 } ], "schema": { "type": "array", "itemType": "object", "itemSchema": { "properties": { "name": { "type": "string" }, "qty": { "type": "number" }, "price": { "type": "number" } } } } }, { "id": "var_total", "name": "orderTotal", "type": "number", "required": true, "defaultValue": 99.99 } ] ``` ### Email Schema ```json { "sections": [ { "id": "body-section", "type": "body", "props": { "backgroundColor": "#f9fafb", "padding": "40px 20px" }, "components": [ { "id": "main-container", "type": "container", "props": { "backgroundColor": "#ffffff", "maxWidth": "600px", "margin": "0 auto", "borderRadius": "8px", "padding": "32px" }, "children": [ { "id": "greeting", "type": "rich-text", "props": { "marginBottom": "24px" }, "content": "

Hello {{customer.name}}

" }, { "id": "intro", "type": "rich-text", "props": { "marginBottom": "16px" }, "content": "

Thank you for your order:

" }, { "id": "items-loop", "type": "loop", "props": { "dataSource": "{{items}}", "itemVariable": "item" }, "children": [ { "id": "item-row", "type": "rich-text", "content": "

• {{item.name}} x {{item.qty}} = ${{item.price}}

" } ] }, { "id": "total-row", "type": "rich-text", "props": { "marginTop": "16px" }, "content": "

Total: ${{orderTotal}}

" } ] } ] } ] } ``` ## AI Generation Checklist Before outputting a template, verify: - [ ] Variables are a **plain array** `[...]`, not `{ "variables": [...] }` - [ ] Every variable has `name` and `type` fields - [ ] Every variable has a `defaultValue` for testing - [ ] Array variables have `itemType` and `schema.itemSchema.properties` - [ ] Object variables have `schema.properties` - [ ] Schema has `sections` array (email) or `pages` array (attachment) - [ ] Every component has a unique `id` - [ ] Variable references use `{{variableName}}` syntax - [ ] Loop components use `{{arrayVariable}}` for `dataSource` - [ ] Nested object access uses dot notation: `{{customer.name}}` ## How to Import into FormaMail --- # Recipes Source: /recipes # Recipes Real-world solutions for common transactional email and document generation use cases. Each recipe includes complete code examples that you can copy and adapt for your needs. ## Featured Recipe
### [Replace SendGrid + DocRaptor with One API Call](/recipes/replace-sendgrid) **The Problem**: You're using 3-4 services (SendGrid, DocRaptor/Puppeteer, S3) and 25+ lines of code to send an email with a PDF attachment. **The Solution**: One API call. No PDF server. No S3 bucket. See the before/after comparison and migration guide.
## All Recipes ### [B2B Invoice Email](/recipes/invoice-email) Send order confirmations with professionally formatted PDF invoices attached. Perfect for e-commerce and SaaS billing. ### [Weekly Excel Report](/recipes/weekly-report) Automate weekly analytics reports with Excel spreadsheet attachments. Great for B2B dashboards and reporting. ### [Order Confirmation + Receipt](/recipes/order-confirmation) E-commerce order confirmations with PDF receipts. Includes itemized orders, shipping details, and payment confirmation. ## Recipe Structure Each recipe follows the same format: 1. **The Problem** - What pain point does this solve? 2. **The Solution** - One-paragraph overview 3. **Prerequisites** - What you need before starting 4. **Step-by-Step Guide** - Implementation walkthrough 5. **Full Code Example** - Copy-paste ready code 6. **Expected Output** - What the result looks like ## Suggest a Recipe Have a use case that isn't covered? Email us at support@formamail.com with your suggestion. --- # B2B Invoice Email Source: /recipes/invoice-email # B2B Invoice Email Send professional invoice emails with PDF invoices attached. Perfect for e-commerce, SaaS billing, and B2B transactions. ## The Problem Sending invoice emails with PDF attachments typically requires: - Rendering the email body - Generating a PDF invoice - Attaching the PDF to the email - Managing multiple templates (email + PDF) - Keeping both templates in sync ## The Solution FormaMail lets you create email and PDF invoice templates, then send emails with dynamically generated PDF attachments in one API call. ## Prerequisites - FormaMail account with API key - Email template created (or use our starter template) - PDF attachment template created ## Step-by-Step Guide ## Full Code Example ### Node.js / Express ```javascript const express = require('express'); const app = express(); app.post('/api/send-invoice', async (req, res) => { const { customer, invoice } = req.body; try { const response = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "invoice-email", to: [{ email: customer.email, name: customer.name }], variables: { invoiceNumber: invoice.number, customerName: customer.name, companyName: customer.company, dueDate: invoice.dueDate, items: invoice.lineItems, subtotal: invoice.subtotal, taxRate: invoice.taxRate, tax: invoice.tax, total: invoice.total }, attachments: [{ attachmentTemplateId: "invoice-pdf", filename: `invoice-${invoice.number}.pdf` }] }) }); const result = await response.json(); if (response.ok) { res.json({ success: true, emailId: result.emailId, message: 'Invoice sent successfully' }); } else { res.status(400).json({ success: false, error: result.message }); } } catch (error) { res.status(500).json({ success: false, error: 'Failed to send invoice' }); } }); ``` ### Python / FastAPI ```python from fastapi import FastAPI, HTTPException app = FastAPI() @app.post("/api/send-invoice") async def send_invoice(customer: dict, invoice: dict): async with httpx.AsyncClient() as client: response = await client.post( 'https://api.formamail.com/api/emails/send', headers={ 'Authorization': f"Bearer {os.environ['FORMAMAIL_API_KEY']}", 'Content-Type': 'application/json' }, json={ 'templateId': 'invoice-email', 'to': [{'email': customer['email'], 'name': customer['name']}], 'variables': { 'invoiceNumber': invoice['number'], 'customerName': customer['name'], 'companyName': customer['company'], 'dueDate': invoice['due_date'], 'items': invoice['line_items'], 'subtotal': invoice['subtotal'], 'taxRate': invoice['tax_rate'], 'tax': invoice['tax'], 'total': invoice['total'] }, 'attachments': [{ 'attachmentTemplateId': 'invoice-pdf', 'filename': f"invoice-{invoice['number']}.pdf" }] } ) if response.status_code != 200: raise HTTPException(status_code=400, detail='Failed to send invoice') return {'success': True, 'emailId': response.json()['emailId']} ``` ## Template Tips ### Line Items Loop Use Handlebars `{{#each}}` to render line items in both email and PDF: ```handlebars {{#each items}} {{description}} {{quantity}} ${{unitPrice}} ${{total}} {{/each}} ``` ### Number Formatting Format currency values with calculated variables: ```javascript variables: { total: 7920, totalFormatted: "$7,920.00" // Pre-format in your code } ``` ### Conditional Content Show different payment terms based on customer type: ```handlebars {{#if isPreferred}} Payment due within 45 days. {{else}} Payment due within 30 days. {{/if}} ``` ## Next Steps - [Create your email template](/user-guide/templates) - [Learn about PDF generation](/developer-guide/attachments) - [See the API reference](/api-reference/emails) --- # Order Confirmation + Receipt Source: /recipes/order-confirmation # Order Confirmation + Receipt Send beautiful order confirmation emails with PDF receipts attached. The classic e-commerce use case that FormaMail was built for. ## The Problem After a customer places an order, you need to: 1. Send a confirmation email immediately 2. Include order details in the email body 3. Attach a printable PDF receipt 4. Make both look professional and consistent Traditional approach requires multiple services and significant code. ## The Solution One API call sends the email with a dynamically generated PDF receipt. Provide the data once in the API request, and FormaMail handles the rest. ## Prerequisites - FormaMail account with API key - Order confirmation email template - PDF receipt template ## Quick Example ```javascript // Send order confirmation with PDF receipt await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "order-confirmation", to: [{ email: order.customerEmail, name: order.customerName }], variables: { orderNumber: order.id, orderDate: new Date().toLocaleDateString(), customerName: order.customerName, shippingAddress: order.shippingAddress, items: order.items, subtotal: order.subtotal, shipping: order.shippingCost, tax: order.tax, total: order.total, estimatedDelivery: order.estimatedDelivery }, attachments: [{ attachmentTemplateId: "order-receipt-pdf", filename: `receipt-${order.id}.pdf` }] }) }); ``` ## Step-by-Step Guide ### Integrate with Your Checkout Flow Add the FormaMail API call after successful payment: ```javascript // In your checkout completion handler async function handleOrderComplete(order, paymentResult) { // 1. Save order to database await saveOrder(order); // 2. Send confirmation email with receipt try { const emailResponse = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "order-confirmation", to: [{ email: order.customer.email, name: order.customer.name }], variables: formatOrderVariables(order), attachments: [{ attachmentTemplateId: "order-receipt-pdf", filename: `receipt-${order.id}.pdf` }] }) }); const result = await emailResponse.json(); // 3. Store email ID for reference await updateOrder(order.id, { confirmationEmailId: result.emailId }); } catch (error) { // Log error but don't fail the order console.error('Failed to send confirmation email:', error); // Queue for retry await queueEmailRetry(order.id); } return { success: true, orderId: order.id }; } function formatOrderVariables(order) { return { orderNumber: order.id, orderDate: new Date(order.createdAt).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }), customerName: order.customer.name, customerEmail: order.customer.email, items: order.items.map(item => ({ name: item.productName, quantity: item.quantity, unitPrice: item.price.toFixed(2), total: (item.quantity * item.price).toFixed(2), imageUrl: item.imageUrl })), subtotal: order.subtotal.toFixed(2), shipping: order.shipping.toFixed(2), tax: order.tax.toFixed(2), total: order.total.toFixed(2), shippingAddress: { name: order.shippingAddress.name, street: order.shippingAddress.street, city: order.shippingAddress.city, state: order.shippingAddress.state, zip: order.shippingAddress.zip, country: order.shippingAddress.country }, paymentMethod: `****${order.payment.lastFour}`, estimatedDelivery: order.estimatedDelivery }; } ``` ## Full Code Examples ### Express.js E-commerce Backend ```javascript const express = require('express'); const router = express.Router(); router.post('/checkout/complete', async (req, res) => { const { cartId, paymentIntentId, shippingAddress } = req.body; try { // Verify payment const payment = await stripe.paymentIntents.retrieve(paymentIntentId); if (payment.status !== 'succeeded') { return res.status(400).json({ error: 'Payment not completed' }); } // Get cart and create order const cart = await Cart.findById(cartId).populate('items.product'); const order = await Order.create({ customer: req.user._id, items: cart.items.map(item => ({ product: item.product._id, productName: item.product.name, quantity: item.quantity, price: item.product.price, imageUrl: item.product.imageUrl })), shippingAddress, payment: { intentId: paymentIntentId, lastFour: payment.payment_method_details.card.last4 }, subtotal: cart.subtotal, shipping: cart.shippingCost, tax: cart.tax, total: cart.total, estimatedDelivery: calculateDeliveryDate() }); // Send confirmation email const emailResult = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "order-confirmation", to: [{ email: req.user.email, name: req.user.name }], variables: { orderNumber: order._id.toString(), orderDate: new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }), customerName: req.user.name, items: order.items.map(item => ({ name: item.productName, quantity: item.quantity, unitPrice: item.price.toFixed(2), total: (item.quantity * item.price).toFixed(2) })), subtotal: order.subtotal.toFixed(2), shipping: order.shipping.toFixed(2), tax: order.tax.toFixed(2), total: order.total.toFixed(2), shippingAddress: { name: shippingAddress.name, street: shippingAddress.line1, city: shippingAddress.city, state: shippingAddress.state, zip: shippingAddress.postal_code }, paymentMethod: `Visa ending in ${payment.payment_method_details.card.last4}`, estimatedDelivery: order.estimatedDelivery }, attachments: [{ attachmentTemplateId: "order-receipt-pdf", filename: `receipt-${order._id}.pdf` }] }) }); // Clear cart await Cart.findByIdAndDelete(cartId); res.json({ success: true, orderId: order._id, message: 'Order placed successfully' }); } catch (error) { console.error('Checkout error:', error); res.status(500).json({ error: 'Failed to complete order' }); } }); function calculateDeliveryDate() { const delivery = new Date(); delivery.setDate(delivery.getDate() + 5); // 5-day shipping return delivery.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }); } ``` ### Shopify Webhook Handler ```javascript // Handle Shopify order creation webhook app.post('/webhooks/shopify/order-created', async (req, res) => { const order = req.body; // Verify webhook signature if (!verifyShopifyWebhook(req)) { return res.status(401).send('Invalid signature'); } // Send confirmation via FormaMail await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "shopify-order-confirmation", to: [{ email: order.email, name: `${order.customer.first_name} ${order.customer.last_name}` }], variables: { orderNumber: order.name, // e.g., "#1001" orderDate: new Date(order.created_at).toLocaleDateString(), customerName: order.customer.first_name, items: order.line_items.map(item => ({ name: item.title, variant: item.variant_title, quantity: item.quantity, price: item.price })), subtotal: order.subtotal_price, shipping: order.total_shipping_price_set.shop_money.amount, tax: order.total_tax, total: order.total_price, shippingAddress: order.shipping_address }, attachments: [{ attachmentTemplateId: "order-receipt-pdf", filename: `receipt-${order.name.replace('#', '')}.pdf` }] }) }); res.status(200).send('OK'); }); ``` ## Template Tips ### Show Item Images Include product images in your email: ```handlebars {{#each items}}
{{name}}
{{name}}

Qty: {{quantity}} × ${{unitPrice}}

{{/each}} ``` ### Conditional Content Show different messages based on order value: ```handlebars {{#if freeShipping}}

You qualified for FREE shipping!

{{else}}

Shipping: ${{shipping}}

{{/if}} ``` ### Multiple Addresses Support gift orders with separate billing/shipping: ```javascript variables: { shippingAddress: order.shippingAddress, billingAddress: order.billingAddress, isGift: order.shippingAddress.email !== order.billingAddress.email, giftMessage: order.giftMessage } ``` ## Next Steps - [Create your order template](/user-guide/templates) - [Set up webhooks for order tracking](/integrations/webhooks) - [Learn about bulk sending](/api-reference/emails#bulk-send) --- # Weekly Excel Report Source: /recipes/weekly-report # Weekly Excel Report Send automated weekly reports with Excel spreadsheet attachments. Perfect for B2B analytics, sales reports, and dashboard summaries. ## The Problem Automating weekly reports typically requires: - Querying data from your database - Generating an Excel file (using libraries like ExcelJS or openpyxl) - Composing an email with a summary - Attaching the Excel file - Scheduling the job (cron, cloud functions) ## The Solution FormaMail generates Excel attachments from templates. You provide the data, we generate the Excel file and attach it to the email - all in one API call. ## Prerequisites - FormaMail account with API key - Email template for the report summary - Excel attachment template ## Step-by-Step Guide ## Full Code Example ### Node.js with Scheduled Job ```javascript const cron = require('node-cron'); // Run every Monday at 9 AM cron.schedule('0 9 * * 1', async () => { console.log('Sending weekly report...'); try { // Get last week's date range const endDate = new Date(); endDate.setDate(endDate.getDate() - 1); // Yesterday const startDate = new Date(endDate); startDate.setDate(startDate.getDate() - 6); // 7 days ago // Query your analytics const analytics = await db.query(` SELECT DATE(created_at) as date, COUNT(*) as orders, SUM(total) as revenue FROM orders WHERE created_at BETWEEN $1 AND $2 GROUP BY DATE(created_at) ORDER BY date `, [startDate, endDate]); const topProducts = await db.query(` SELECT product_name, SUM(quantity) as units_sold, SUM(total) as revenue FROM order_items oi JOIN orders o ON o.id = oi.order_id WHERE o.created_at BETWEEN $1 AND $2 GROUP BY product_name ORDER BY revenue DESC LIMIT 10 `, [startDate, endDate]); // Calculate totals const totalRevenue = analytics.reduce((sum, day) => sum + day.revenue, 0); const totalOrders = analytics.reduce((sum, day) => sum + day.orders, 0); // Send via FormaMail await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "weekly-report-email", to: [ { email: "sales@company.com" }, { email: "ceo@company.com" } ], variables: { reportPeriod: `${formatDate(startDate)} - ${formatDate(endDate)}`, totalRevenue: totalRevenue.toFixed(2), totalOrders: totalOrders, averageOrderValue: (totalRevenue / totalOrders).toFixed(2), topProducts: topProducts.rows, dailyBreakdown: analytics.rows.map(row => ({ date: formatDate(row.date), orders: row.orders, revenue: row.revenue.toFixed(2) })) }, attachments: [{ attachmentTemplateId: "weekly-report-excel", outputFormats: ["excel"], filename: `weekly-report-${formatDateShort(startDate)}.xlsx` }] }) }); console.log('Weekly report sent successfully'); } catch (error) { console.error('Failed to send weekly report:', error); } }); function formatDate(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } function formatDateShort(date) { return date.toISOString().split('T')[0]; } ``` ### Python with AWS Lambda ```python from datetime import datetime, timedelta def lambda_handler(event, context): """AWS Lambda function triggered by CloudWatch Events (weekly schedule)""" # Calculate date range end_date = datetime.now() - timedelta(days=1) start_date = end_date - timedelta(days=6) # Query your data source (example with DynamoDB) dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('orders') # ... query logic ... # Prepare report data report_data = { 'reportPeriod': f"{start_date.strftime('%b %d')} - {end_date.strftime('%b %d, %Y')}", 'totalRevenue': 15420.50, 'totalOrders': 127, 'topProducts': [ {'name': 'Widget Pro', 'units': 45, 'revenue': 4500}, {'name': 'Gadget Plus', 'units': 32, 'revenue': 3200}, ], 'dailyBreakdown': [ {'date': 'Mon', 'orders': 18, 'revenue': 2100}, {'date': 'Tue', 'orders': 22, 'revenue': 2450}, # ... more days ] } # Send via FormaMail response = httpx.post( 'https://api.formamail.com/api/emails/send', headers={ 'Authorization': f"Bearer {os.environ['FORMAMAIL_API_KEY']}", 'Content-Type': 'application/json' }, json={ 'templateId': 'weekly-report-email', 'to': [{'email': 'team@company.com'}], 'variables': report_data, 'attachments': [{ 'attachmentTemplateId': 'weekly-report-excel', 'outputFormats': ['excel'], 'filename': f"weekly-report-{start_date.strftime('%Y-%m-%d')}.xlsx" }] } ) return { 'statusCode': 200, 'body': json.dumps('Weekly report sent') } ``` ## Excel Template Tips ### Multiple Sheets Your Excel template can include multiple sheets: - **Summary** - Key metrics and highlights - **Daily Breakdown** - Day-by-day data - **Top Products** - Product performance - **Raw Data** - Complete dataset for further analysis ### Data Tables Use the `{{#each}}` helper to populate rows: ``` | Date | Orders | Revenue | |------|--------|---------| {{#each dailyBreakdown}} | {{date}} | {{orders}} | ${{revenue}} | {{/each}} ``` ### Formatting Numbers Pre-format numbers in your code or use template helpers: ```javascript variables: { revenue: 15420.50, revenueFormatted: '$15,420.50', percentChange: '+12.5%' } ``` ## Scheduling Options | Platform | How to Schedule | |----------|-----------------| | AWS Lambda | CloudWatch Events rule | | Google Cloud Functions | Cloud Scheduler | | Azure Functions | Timer trigger | | Vercel | Cron jobs (vercel.json) | | Railway/Render | Built-in cron | | Self-hosted | node-cron, systemd timer | ## Next Steps - [Create an Excel template](/user-guide/attachment-templates) - [Learn about output formats](/developer-guide/attachments) - [Explore bulk sending](/api-reference/emails#bulk-send) --- # Integrations Source: /integrations # Integrations Connect FormaMail to your applications, workflows, and automation platforms with our native integrations and official SDKs. ## Integration Options

No-Code & SDKs

Native integrations for Zapier, Make.com, n8n plus official Node.js and Python SDKs.

Webhook Subscriptions

Receive real-time notifications when emails are sent, delivered, opened, bounced, or clicked.

## Native Integrations FormaMail provides **official integrations** for popular platforms: | Platform | Type | Authentication | Status | |----------|------|---------------|--------| | **Zapier** | Native Integration | OAuth 2.0 | Available | | **Make.com** | Native Integration | OAuth 2.0 | Available | | **n8n** | Community Node | API Key | Available | | **Node.js SDK** | `@formamail/sdk` | API Key | Available | | **Python SDK** | `formamail` | API Key | Available | ## Official SDKs Install our official SDKs for the best developer experience: ### Node.js ```bash npm install @formamail/sdk ``` ```typescript const client = new Formamail({ apiKey: process.env.FORMAMAIL_API_KEY }); await client.emails.send({ templateId: 'welcome-email', to: 'user@example.com', variables: { name: 'John' } }); ``` ### Python ```bash pip install formamail ``` ```python from formamail import Formamail client = Formamail(api_key=os.environ["FORMAMAIL_API_KEY"]) client.emails.send( template_id="welcome-email", to="user@example.com", variables={"name": "John"} ) ``` [Full SDK Documentation →](/integrations/no-code#native-sdks) ## No-Code Platforms ### Zapier - 6 Triggers: Email Sent, Delivered, Opened, Clicked, Bounced, Unsubscribe - 4 Actions: Send Email, Send with PDF, Send with Excel, Send Bulk - 1 Search: Find Email by ID/recipient/status ### Make.com (Integromat) - 6 Watch Modules for email events - 4 Action Modules for sending emails - Visual workflow builder integration ### n8n - FormaMail Trigger node for all email events - FormaMail Action node for sending operations - Simple API Key authentication [Full Integration Documentation →](/integrations/no-code) ## Webhook Subscriptions Subscribe to real-time email events for custom integrations: | Event | Description | |-------|-------------| | `email.sent` | Email successfully queued for delivery | | `email.delivered` | Email delivered to recipient's mailbox | | `email.bounced` | Email bounced (hard or soft) | | `email.complained` | Recipient marked as spam | | `email.opened` | Recipient opened the email | | `email.clicked` | Recipient clicked a link | | `unsubscribe.created` | Recipient unsubscribed | ### Webhook Security All webhooks are signed with HMAC-SHA256 for verification: ```javascript const event = verifyWebhookSignature({ payload: requestBody, signature: headers['x-formamail-signature'], secret: process.env.WEBHOOK_SECRET }); ``` [Full Webhook Documentation →](/integrations/webhooks) ## Common Integration Patterns ### Form Submission → Welcome Email When someone submits a form (Webflow, Typeform, Google Forms), automatically send them a welcome email. **Zapier**: Form Submitted (Trigger) → FormaMail Send Email (Action) ### Payment → Invoice Email with PDF When a payment succeeds (Stripe, PayPal, Cashfree), send an invoice email with PDF attachment. **Zapier**: Payment Succeeded (Trigger) → FormaMail Send Email with PDF (Action) ### Email Bounced → Update CRM When an email bounces, automatically update the contact status in your CRM. **Zapier**: FormaMail Email Bounced (Trigger) → HubSpot Update Contact (Action) ### Schedule → Report Email with Excel Send weekly/monthly reports via email with Excel attachments. **Zapier**: Schedule Weekly (Trigger) → FormaMail Send Email with Excel (Action) ## Quick Start 1. **Choose your integration method**: - SDK for programmatic access - Zapier/Make/n8n for no-code automation - Direct API for custom implementations 2. **Authenticate**: - SDKs & n8n: Create an API key in your dashboard - Zapier & Make.com: Connect via OAuth 2.0 3. **Start building**: - Send emails with templates - Generate PDF/Excel attachments - Subscribe to webhook events [Getting Started →](/getting-started) [API Reference →](/api-reference) --- # No-Code Integrations Source: /integrations/no-code # No-Code & Low-Code Integrations Connect FormaMail to thousands of apps using our native integrations. Build automated workflows that send transactional emails and dynamically generate PDF and Excel attachments. ## Overview FormaMail provides **native integrations** for popular automation platforms: --- ## Zapier Integration ### Getting Started with Zapier ### Available Triggers Triggers start your Zap when an event occurs in FormaMail: | Trigger | Description | |---------|-------------| | **Email Sent** | When an email is successfully queued for delivery | | **Email Delivered** | When an email reaches the recipient's mailbox | | **Email Opened** | When a recipient opens an email (tracking required) | | **Link Clicked** | When a recipient clicks a tracked link | | **Email Bounced** | When an email bounces (hard or soft) | | **Unsubscribe Received** | When a recipient unsubscribes from emails | ### Available Actions Actions let you perform operations in FormaMail: | Action | Description | |--------|-------------| | **Send Email** | Send an email using a FormaMail template | | **Send Email with PDF** | Send email with auto-generated PDF attachment | | **Send Email with Excel** | Send email with auto-generated Excel attachment | | **Send Bulk Email** | Send personalized emails to multiple recipients | ### Available Searches Searches let you find existing data in FormaMail: | Search | Description | |--------|-------------| | **Find Email** | Search for an email by ID, recipient, status, or date | ### Common Zapier Workflows #### New Form Submission → Welcome Email **Trigger:** Google Forms / Typeform / Webflow **Action:** FormaMail - Send Email ``` New form submitted → Send welcome email with personalized name ``` **Configuration:** 1. Select trigger (e.g., "New Form Response" in Google Forms) 2. Add FormaMail "Send Email" action 3. Choose your welcome email template 4. Map form fields to template variables: - `{{email}}` → Form email field - `{{name}}` → Form name field - `{{firstName}}` → Form first name field #### Payment Received → Invoice Email with PDF **Trigger:** Stripe - Payment Succeeded **Action:** FormaMail - Send Email with PDF ``` Payment successful → Generate invoice PDF → Send via email ``` **Configuration:** 1. Trigger: Stripe "Payment Intent Succeeded" 2. Action: FormaMail "Send Email with PDF" 3. Select email template and PDF template 4. Map payment data: - `{{invoiceNumber}}` → Stripe payment ID - `{{amount}}` → Payment amount - `{{customerName}}` → Customer name #### Email Bounced → Update CRM **Trigger:** FormaMail - Email Bounced **Action:** HubSpot - Update Contact ``` Email bounces → Mark contact as undeliverable in CRM ``` #### Weekly Schedule → Report Email with Excel **Trigger:** Schedule by Zapier (Weekly) **Action:** FormaMail - Send Email with Excel ``` Every Monday 9 AM → Generate report → Send to stakeholders ``` ### Trigger Data Fields When FormaMail triggers fire, they provide these data fields: ```json { "event": "email.delivered", "timestamp": "2025-12-02T10:30:00Z", "data": { "emailLogId": "el_abc123...", "recipient": "user@example.com", "recipientName": "John Doe", "subject": "Your Invoice", "templateId": "tmpl_invoice", "status": "delivered" } } ``` --- ## Make.com Integration ### Getting Started with Make.com ### Available Modules #### Triggers (Watch Events) | Module | Description | |--------|-------------| | **Watch Email Sent** | Triggers when email is sent | | **Watch Email Delivered** | Triggers when email is delivered | | **Watch Email Opened** | Triggers when email is opened | | **Watch Link Clicked** | Triggers when link is clicked | | **Watch Email Bounced** | Triggers when email bounces | | **Watch Unsubscribe** | Triggers when user unsubscribes | #### Actions | Module | Description | |--------|-------------| | **Send Email** | Send email with template | | **Send Email with PDF** | Email + generated PDF | | **Send Email with Excel** | Email + generated Excel | | **Send Bulk Email** | Multiple recipients | #### Searches | Module | Description | |--------|-------------| | **Get Email** | Retrieve email by ID | ### Make.com Scenario Examples #### Multi-Condition Email Router Use the Router module to send different emails based on conditions: ``` Trigger → Router ├─→ [Order > $100] → Send VIP Welcome ├─→ [Trial User] → Send Trial Onboarding └─→ [Default] → Send Standard Welcome ``` #### Batch Processing with Iterator Process multiple records with intelligent rate limiting: ``` 1. Get Records (from database/sheet) 2. Array Aggregator 3. Iterator 4. FormaMail - Send Email 5. Sleep (rate limit buffer) ``` #### Error Handling Flow ``` FormaMail Send Email ├─→ Success → Continue workflow └─→ Error Handler ├─→ 429 Rate Limit → Wait & Retry └─→ 400 Bad Request → Log to Sheet ``` --- ## n8n Integration ### Installation ### Setting Up Credentials ### Available Nodes #### FormaMail Trigger Node Starts workflows on email events: | Event | Description | |-------|-------------| | Email Sent | Email queued for delivery | | Email Delivered | Email reached mailbox | | Email Opened | Recipient opened email | | Link Clicked | Recipient clicked link | | Email Bounced | Email bounced | | Unsubscribe | Recipient unsubscribed | #### FormaMail Action Node Performs operations: | Operation | Description | |-----------|-------------| | Send | Send email with template | | Send with PDF | Email + PDF attachment | | Send with Excel | Email + Excel attachment | | Send Bulk | Multiple recipients | | Get | Get email details | | Search | Search emails | ### n8n Workflow Examples #### Multi-Step Email Sequence ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Webhook │ ──→ │ Send Email │ ──→ │ Wait 24h │ │ (Signup) │ │ (Welcome) │ │ │ └─────────────┘ └──────────────┘ └──────┬──────┘ │ ┌─────────────┐ ┌──────────────┐ ┌──────┴──────┐ │ Wait 48h │ ←── │ Send Email │ ←── │ Send Email │ │ │ │ (Follow-up) │ │ (Onboarding)│ └─────────────┘ └──────────────┘ └─────────────┘ ``` #### Conditional Logic with Switch Node ``` ┌──────────┐ ┌──────────┐ ┌────────────────────┐ │ Trigger │ ──→ │ Switch │ ──→ │ Customer → VIP │ │ │ │ (type) │ │ Partner → Partner │ └──────────┘ └──────────┘ │ Default → Standard │ └────────────────────┘ ``` #### Error Handling & Retry ``` ┌──────────────┐ ┌──────────────┐ │ FormaMail │ ──→ │ Success │ │ Send Email │ │ Continue │ └──────┬───────┘ └──────────────┘ │ │ (Error) ↓ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Error │ ──→ │ Wait 5min │ ──→ │ Retry │ │ Trigger │ │ │ │ Send Email │ └──────────────┘ └──────────────┘ └──────────────┘ ``` ### Self-Hosted Benefits - **Full API Access**: Use all FormaMail API endpoints - **Custom Nodes**: Extend functionality as needed - **Data Privacy**: Keep workflow data on your infrastructure - **Unlimited Workflows**: No execution limits - **Advanced Logic**: Complex branching and error handling --- ## Native SDKs FormaMail provides official SDKs for popular programming languages: ### Node.js SDK **Package:** `@formamail/sdk` ```bash npm install @formamail/sdk ``` #### Webhook Verification (Node.js) ```typescript app.post('/webhooks/formamail', express.raw({ type: 'application/json' }), (req, res) => { try { const event = verifyWebhookSignature({ payload: req.body.toString(), signature: req.headers['x-formamail-signature'] as string, secret: process.env.WEBHOOK_SECRET!, }); switch (event.type) { case 'email.delivered': console.log('Delivered:', event.data.emailId); break; case 'email.bounced': console.log('Bounced:', event.data.emailId); break; } res.status(200).send('OK'); } catch (error) { res.status(400).send('Invalid signature'); } }); ``` ### Python SDK **Package:** `formamail` ```bash pip install formamail ``` ### SDK API Reference #### Client Methods | Method | Description | |--------|-------------| | `client.me()` | Get authenticated user info | | `client.verifyApiKey()` | Verify API key is valid | #### Emails Resource | Method | Description | |--------|-------------| | `emails.send()` | Send single email | | `emails.sendWithPdf()` | Send with PDF attachment | | `emails.sendWithExcel()` | Send with Excel attachment | | `emails.sendBulk()` | Send bulk emails | | `emails.get(id)` | Get email by ID | | `emails.list()` | List/search emails | #### Templates Resource | Method | Description | |--------|-------------| | `templates.get(id)` | Get template | | `templates.list()` | List all templates | | `templates.listEmail()` | List email templates | | `templates.listPdf()` | List PDF templates | | `templates.listExcel()` | List Excel templates | #### Webhooks Resource | Method | Description | |--------|-------------| | `webhooks.create()` | Create subscription | | `webhooks.get(id)` | Get webhook | | `webhooks.list()` | List webhooks | | `webhooks.update()` | Update webhook | | `webhooks.delete(id)` | Delete webhook | --- ## Webhook Events All integrations can subscribe to these webhook events: | Event | Description | Data Fields | |-------|-------------|-------------| | `email.sent` | Email queued for delivery | emailId, recipient, subject, templateId | | `email.delivered` | Email reached mailbox | emailId, recipient, deliveredAt | | `email.opened` | Recipient opened email | emailId, recipient, openedAt, device | | `email.clicked` | Link clicked | emailId, recipient, linkUrl, clickedAt | | `email.bounced` | Email bounced | emailId, recipient, bounceType, reason | | `unsubscribe.created` | Recipient unsubscribed | email, unsubscribedAt, category | ### Webhook Payload Format ```json { "event": "email.delivered", "timestamp": "2025-12-02T10:30:00.000Z", "data": { "emailLogId": "el_abc123def456", "recipient": "user@example.com", "recipientName": "John Doe", "subject": "Welcome to FormaMail", "templateId": "tmpl_welcome", "status": "delivered", "deliveredAt": "2025-12-02T10:30:00.000Z" } } ``` ### Webhook Security All webhooks include security headers for signature verification: | Header | Description | |--------|-------------| | `X-FormaMail-Signature` | HMAC-SHA256 signature (`v1=`) | | `X-FormaMail-Timestamp` | Unix timestamp of request | | `X-FormaMail-Event-Id` | Unique event identifier | | `X-FormaMail-Event-Type` | Event type | --- ## Authentication ### OAuth 2.0 (Zapier & Make.com) Zapier and Make.com use OAuth 2.0 for secure authentication. **Scopes:** | Scope | Description | |-------|-------------| | `emails:send` | Send emails | | `emails:read` | Read email status | | `templates:read` | Access templates | | `webhooks:read` | View webhook subscriptions | | `webhooks:write` | Manage webhook subscriptions | | `profile:read` | Read user profile | ### API Keys (n8n & SDKs) n8n and native SDKs authenticate using API keys generated in the FormaMail dashboard. 1. Go to **Settings** → **API Keys** 2. Click **Create API Key** 3. Copy the key (shown only once) 4. Store securely in environment variables ```bash # .env FORMAMAIL_API_KEY=fm_sk_xxxxxxxxxxxx ``` --- ## Rate Limits | Tier | Requests/Second | Emails/Month | |------|-----------------|--------------| | Free | 1 | 100 | | Starter | 10 | 10,000 | | Pro | 50 | 100,000 | | Enterprise | Custom | Custom | --- ## Best Practices ### 1. API Key Security - Store keys in environment variables - Use platform secret managers - Rotate keys periodically - Use separate keys for development/production ### 2. Error Handling Always implement error handling: ```javascript try { await formamail.emails.send({...}); } catch (error) { if (error.statusCode === 429) { // Rate limit - implement backoff } else if (error.statusCode === 400) { // Validation error - check request } } ``` ### 3. Webhook Verification **Always verify webhook signatures** to prevent spoofing attacks. ### 4. Use Bulk Endpoints For multiple recipients, use bulk send instead of individual requests: ```javascript // Bad: Many individual requests for (const recipient of recipients) { await formamail.emails.send({ to: recipient.email, ... }); } // Good: Single bulk request await formamail.emails.sendBulk({ recipients: recipients.map(r => ({ email: r.email, ... })), }); ``` --- ## Troubleshooting ### Common Issues **401 Unauthorized** - Verify API key or OAuth token is valid - Check key has required permissions - Ensure header is `Authorization` with value `Bearer YOUR_API_KEY` **400 Bad Request** - Validate JSON structure - Check required fields (templateId, to) - Verify template exists **429 Rate Limit** - Implement exponential backoff - Use bulk endpoints - Contact support for limit increase **Webhook Not Receiving Events** - Verify URL is publicly accessible (HTTPS) - Check subscription is active - Review delivery logs in dashboard ### Getting Help - **Documentation**: https://docs.formamail.com - **API Reference**: https://docs.formamail.com/api - **Support Email**: support@formamail.com - **Status Page**: status.formamail.com --- ## Next Steps --- # Webhook Subscriptions Source: /integrations/webhooks # Webhook Subscriptions Receive real-time HTTP notifications when email events occur. Webhooks eliminate the need to poll for status updates. ## Overview When you create a webhook subscription, FormaMail will send HTTP POST requests to your specified URL whenever matching events occur. ### Supported Events | Event | Description | |-------|-------------| | `email.sent` | Email successfully queued for delivery | | `email.delivered` | Email delivered to recipient's mailbox | | `email.bounced` | Email bounced (includes bounce type) | | `email.complained` | Recipient marked email as spam | | `email.opened` | Recipient opened the email (if tracking enabled) | | `email.clicked` | Recipient clicked a link (if tracking enabled) | ## Getting Started ### Create a Webhook Subscription Use the API or dashboard to create a subscription: ### Verify Webhook Signatures Always verify webhook signatures to ensure requests are from FormaMail: ```javascript const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, timestamp, secret) { const signedPayload = `${timestamp}.${JSON.stringify(payload)}`; const expectedSignature = crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex'); return signature === `v1=${expectedSignature}`; } app.post('/webhooks/formamail', express.json(), (req, res) => { const signature = req.headers['x-formamail-signature']; const timestamp = req.headers['x-formamail-timestamp']; if (!verifyWebhookSignature(req.body, signature, timestamp, WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } // Process verified webhook // ... res.status(200).send('OK'); }); ``` ### Test Your Webhook Send a test event to verify your endpoint: ```javascript await fetch(`https://api.formamail.com/api/v1/webhook-subscriptions/${subscriptionId}/test`, { method: 'POST', headers: { 'Authorization': 'Bearer your-api-key', 'Content-Type': 'application/json' } }); ``` ## Webhook Payload Format Every webhook delivery includes these headers and a JSON body: ### Headers | Header | Description | Example | |--------|-------------|---------| | `X-FormaMail-Signature` | HMAC-SHA256 signature | `v1=abc123...` | | `X-FormaMail-Timestamp` | Unix timestamp | `1732723200` | | `X-FormaMail-Event-Id` | Unique event identifier | `evt_xxx` | | `X-FormaMail-Event-Type` | Event type | `email.delivered` | | `Content-Type` | Always JSON | `application/json` | ### Body Structure ```json { "event": "email.delivered", "timestamp": "2024-11-27T12:00:00.000Z", "data": { "emailLogId": "550e8400-e29b-41d4-a716-446655440000", "recipient": "user@example.com", "subject": "Your Order Confirmation", "status": "delivered", "templateId": "order-confirmation", "metadata": { "orderId": "12345" } } } ``` ## Event Payloads ### email.sent ```json { "event": "email.sent", "timestamp": "2024-11-27T12:00:00.000Z", "data": { "emailLogId": "uuid", "recipient": "user@example.com", "subject": "Welcome!", "status": "sent", "templateId": "welcome-email" } } ``` ### email.delivered ```json { "event": "email.delivered", "timestamp": "2024-11-27T12:00:05.000Z", "data": { "emailLogId": "uuid", "recipient": "user@example.com", "subject": "Welcome!", "status": "delivered", "deliveredAt": "2024-11-27T12:00:05.000Z" } } ``` ### email.bounced ```json { "event": "email.bounced", "timestamp": "2024-11-27T12:00:10.000Z", "data": { "emailLogId": "uuid", "recipient": "invalid@example.com", "subject": "Welcome!", "status": "bounced", "bounceType": "hard", "bounceSubType": "NoEmail", "bounceMessage": "Address does not exist" } } ``` ### email.complained ```json { "event": "email.complained", "timestamp": "2024-11-27T12:05:00.000Z", "data": { "emailLogId": "uuid", "recipient": "user@example.com", "subject": "Weekly Newsletter", "status": "complained", "complainedAt": "2024-11-27T12:05:00.000Z", "feedbackType": "abuse" } } ``` ### email.opened ```json { "event": "email.opened", "timestamp": "2024-11-27T12:10:00.000Z", "data": { "emailLogId": "uuid", "recipient": "user@example.com", "subject": "Welcome!", "openedAt": "2024-11-27T12:10:00.000Z", "userAgent": "Mozilla/5.0...", "ipAddress": "192.168.1.1" } } ``` ### email.clicked ```json { "event": "email.clicked", "timestamp": "2024-11-27T12:12:00.000Z", "data": { "emailLogId": "uuid", "recipient": "user@example.com", "subject": "Welcome!", "clickedAt": "2024-11-27T12:12:00.000Z", "url": "https://yourapp.com/get-started", "userAgent": "Mozilla/5.0...", "ipAddress": "192.168.1.1" } } ``` ## API Reference ### List Subscriptions **GET /api/v1/webhook-subscriptions** ```bash curl https://api.formamail.com/api/v1/webhook-subscriptions \ -H "Authorization: Bearer your-api-key" ``` ### Create Subscription **POST /api/v1/webhook-subscriptions** ```json { "url": "https://yourapp.com/webhooks/formamail", "events": ["email.delivered", "email.bounced"], "description": "Production webhook", "enabled": true } ``` Response includes the signing secret: ```json { "id": "wh_xxxxx", "url": "https://yourapp.com/webhooks/formamail", "events": ["email.delivered", "email.bounced"], "secret": "whsec_xxxxxxxxxxxxx", "enabled": true, "createdAt": "2024-11-27T12:00:00.000Z" } ``` ### Get Subscription **GET /api/v1/webhook-subscriptions/:id** ### Update Subscription **PATCH /api/v1/webhook-subscriptions/:id** ```json { "events": ["email.delivered", "email.bounced", "email.opened"], "enabled": true } ``` ### Delete Subscription **DELETE /api/v1/webhook-subscriptions/:id** ### Send Test Webhook **POST /api/v1/webhook-subscriptions/:id/test** Sends a test `email.delivered` event to your endpoint. ### Get Delivery History **GET /api/v1/webhook-subscriptions/:id/deliveries** View recent delivery attempts: ```json { "deliveries": [ { "id": "del_xxxxx", "event": "email.delivered", "status": "success", "statusCode": 200, "responseTime": 150, "attemptedAt": "2024-11-27T12:00:00.000Z" }, { "id": "del_yyyyy", "event": "email.bounced", "status": "failed", "statusCode": 500, "error": "Internal Server Error", "attemptedAt": "2024-11-27T11:55:00.000Z", "nextRetryAt": "2024-11-27T11:56:00.000Z" } ] } ``` ### Retry Failed Delivery **POST /api/v1/webhook-subscriptions/:id/deliveries/:deliveryId/retry** Manually retry a failed delivery. ## Signature Verification ### Algorithm FormaMail uses HMAC-SHA256 for webhook signatures: 1. Construct the signed payload: `{timestamp}.{json_body}` 2. Compute HMAC-SHA256 with your webhook secret 3. Compare with the signature header (after removing `v1=` prefix) ### Code Examples ### Timestamp Validation Prevent replay attacks by validating the timestamp: ```javascript function isTimestampValid(timestamp, toleranceSeconds = 300) { const now = Math.floor(Date.now() / 1000); const eventTime = parseInt(timestamp, 10); return Math.abs(now - eventTime) <= toleranceSeconds; } ``` ## Retry Policy Failed deliveries are automatically retried with exponential backoff: | Attempt | Delay | |---------|-------| | 1 | Immediate | | 2 | 1 minute | | 3 | 5 minutes | | 4 (final) | 30 minutes | After 4 failed attempts, the delivery is marked as failed. You can manually retry from the delivery history. ### What Counts as Failure - Non-2xx response status - Connection timeout (30 seconds) - Connection refused - SSL/TLS errors ## Best Practices ### Respond Quickly Return 200 OK as fast as possible. Process events asynchronously: ```javascript app.post('/webhooks/formamail', express.json(), async (req, res) => { // Verify signature first if (!verifySignature(req.body, req.headers['x-formamail-signature'], req.headers['x-formamail-timestamp'], secret)) { return res.status(401).send('Invalid signature'); } // Acknowledge receipt immediately res.status(200).send('OK'); // Process asynchronously processWebhookAsync(req.body).catch(console.error); }); async function processWebhookAsync(event) { // Your actual processing logic switch (event.event) { case 'email.bounced': await handleBounce(event.data); break; // ... other events } } ``` ### Handle Duplicates Webhooks may be delivered more than once. Use the `X-FormaMail-Event-Id` header for idempotency: ```javascript const processedEvents = new Set(); // Use Redis in production app.post('/webhooks/formamail', async (req, res) => { const eventId = req.headers['x-formamail-event-id']; if (processedEvents.has(eventId)) { return res.status(200).send('Already processed'); } // Process event // ... processedEvents.add(eventId); res.status(200).send('OK'); }); ``` ### Use HTTPS Webhook URLs must use HTTPS. Self-signed certificates are not supported. ### Rotate Secrets If you suspect your webhook secret is compromised, create a new subscription and delete the old one. ## Troubleshooting ### Not Receiving Webhooks 1. **Check subscription is enabled**: View subscription in dashboard 2. **Verify URL is accessible**: Can FormaMail reach your endpoint? 3. **Check firewall rules**: Allow incoming connections 4. **Review delivery history**: Check for failed deliveries ### Signature Verification Failing 1. **Use raw body**: Don't parse JSON before verification 2. **Check secret**: Make sure you're using the correct secret 3. **Check timestamp**: Ensure your server clock is accurate ### Slow Processing 1. **Respond quickly**: Return 200 before processing 2. **Use queue**: Process events asynchronously 3. **Batch updates**: Don't hit your database for every event ## OAuth Authentication Webhook subscriptions can also be created via OAuth-authenticated requests: - **Read**: Requires `webhooks:read` scope - **Create/Update/Delete**: Requires `webhooks:write` scope ```javascript // Using OAuth access token const response = await fetch('https://api.formamail.com/api/v1/webhook-subscriptions', { method: 'POST', headers: { 'Authorization': `Bearer ${oauthAccessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://yourapp.com/webhooks/formamail', events: ['email.delivered'] }) }); ``` --- # Zapier Starter Packs Source: /integrations/zapier-starter-packs # Zapier Starter Packs Starter Packs are pre-built template bundles designed specifically for common Zapier automation workflows. Each pack includes a professional email template and matching PDF or Excel attachment template. ## Available Starter Packs --- ## Using Starter Packs with Zapier --- ## Sales Proposal **Best For:** CRM integrations (HubSpot, Salesforce, Pipedrive) **Trigger Events:** - Deal stage changed to "Proposal Sent" - New deal created - Opportunity won **Attachment Type:** PDF ### Variable Mapping (HubSpot) | Template Variable | HubSpot Field | Description | |-------------------|---------------|-------------| | `customer.name` | `{{Contact: Company}}` | Customer company name | | `customer.email` | `{{Contact: Email}}` | Recipient email | | `deal.name` | `{{Deal: Deal Name}}` | Deal/opportunity name | | `deal.value` | `{{Deal: Amount}}` | Total deal value | | `proposal.validUntil` | Calculated | Use Zapier formatter (today + 30 days) | | `salesRep.name` | `{{Deal: Owner Name}}` | Sales representative | | `salesRep.email` | `{{Deal: Owner Email}}` | Rep's email | ### Sample Zap Configuration ``` Trigger: HubSpot - Deal Stage Changed Filter: Stage = "Proposal Sent" Action: FormaMail - Send Email with PDF ├── Template: sales-proposal-email ├── PDF Template: sales-proposal-pdf ├── To: {{Contact: Email}} └── Variables: Mapped from HubSpot fields ``` --- ## Order Confirmation **Best For:** E-commerce platforms (Shopify, WooCommerce, BigCommerce) **Trigger Events:** - New order placed - Order paid **Attachment Type:** PDF (Invoice/Receipt) ### Variable Mapping (Shopify) | Template Variable | Shopify Field | Description | |-------------------|---------------|-------------| | `customer.name` | `{{Customer: First Name}} {{Customer: Last Name}}` | Full name | | `customer.email` | `{{Customer: Email}}` | Recipient email | | `order.id` | `{{Order: Name}}` | Order number (#1001) | | `order.date` | `{{Order: Created At}}` | Order date | | `items` | `{{Line Items}}` | Array of products | | `subtotal` | `{{Order: Subtotal Price}}` | Subtotal | | `shippingCost` | `{{Order: Total Shipping Price}}` | Shipping | | `tax` | `{{Order: Total Tax}}` | Tax amount | | `total` | `{{Order: Total Price}}` | Grand total | ### Line Items Array For the `items` array, use Zapier's formatter or a custom Code step: ```javascript // In Zapier Code step return { items: inputData.lineItems.map(item => ({ name: item.title, quantity: item.quantity, price: item.price, priceFormatted: `$${item.price}`, sku: item.sku, image: item.image?.src || '' })) }; ``` --- ## Payment Receipt **Best For:** Payment processors (Stripe, PayPal, Square) **Trigger Events:** - Payment succeeded - Invoice paid **Attachment Type:** PDF ### Variable Mapping (Stripe) | Template Variable | Stripe Field | Description | |-------------------|--------------|-------------| | `customer.name` | `{{Customer: Name}}` | Customer name | | `customer.email` | `{{Customer: Email}}` | Recipient email | | `payment.id` | `{{Payment Intent: ID}}` | Transaction ID | | `payment.date` | `{{Payment Intent: Created}}` | Payment date | | `payment.amount` | `{{Payment Intent: Amount}} / 100` | Amount (convert from cents) | | `payment.currency` | `{{Payment Intent: Currency}}` | Currency code | | `payment.method` | `Credit Card` | Payment method | | `payment.last4` | `{{Payment Method: Card Last 4}}` | Last 4 digits | --- ## Shipping Notification **Best For:** E-commerce fulfillment notifications **Trigger Events:** - Order fulfilled - Shipment created **Attachment Type:** PDF (Packing Slip) ### Variable Mapping (Shopify Fulfillment) | Template Variable | Shopify Field | Description | |-------------------|---------------|-------------| | `customer.name` | `{{Customer: Name}}` | Customer name | | `customer.email` | `{{Customer: Email}}` | Recipient email | | `order.id` | `{{Order: Name}}` | Order number | | `shipping.trackingNumber` | `{{Fulfillment: Tracking Number}}` | Tracking number | | `shipping.trackingUrl` | `{{Fulfillment: Tracking URL}}` | Tracking link | | `shipping.carrier` | `{{Fulfillment: Tracking Company}}` | Carrier name | | `shipping.estimatedDelivery` | Calculated | Expected delivery date | --- ## SaaS Invoice **Best For:** Subscription businesses, B2B invoicing **Trigger Events:** - Invoice created - Subscription renewed - Payment received **Attachment Type:** PDF ### Variable Mapping | Template Variable | Source Field | Description | |-------------------|--------------|-------------| | `company.name` | Static | Your company name | | `company.logo` | Static | Your logo URL | | `customer.name` | Trigger data | Customer/company name | | `customer.email` | Trigger data | Billing email | | `invoice.number` | Generated | Invoice number | | `invoice.date` | Current date | Invoice date | | `invoice.dueDate` | Calculated | Due date (net 30) | | `items` | Trigger data | Line items array | | `subtotal` | Calculated | Sum of items | | `tax.rate` | Your tax rate | Tax percentage | | `tax.amount` | Calculated | Tax amount | | `total` | Calculated | Grand total | --- ## Project Status **Best For:** Project management tools (Airtable, Monday.com, Notion) **Trigger Events:** - Project status changed - Milestone completed - Weekly schedule **Attachment Type:** Excel ### Variable Mapping (Airtable) | Template Variable | Airtable Field | Description | |-------------------|----------------|-------------| | `project.name` | `{{Record: Name}}` | Project name | | `project.status` | `{{Record: Status}}` | Current status | | `project.progress` | `{{Record: Progress}}` | 0-100 percentage | | `project.startDate` | `{{Record: Start Date}}` | Start date | | `project.targetDate` | `{{Record: Target Date}}` | Target completion | | `tasks` | Linked records | Task list array | | `metrics` | Formula fields | Summary metrics | --- ## Weekly Report **Best For:** Scheduled reporting from Google Sheets, databases **Trigger Events:** - Schedule (weekly) - New summary row **Attachment Type:** Excel ### Variable Mapping (Google Sheets) | Template Variable | Google Sheets | Description | |-------------------|---------------|-------------| | `report.title` | Cell or static | Report title | | `report.period` | Date range | Reporting period | | `report.generatedAt` | Current date | Generation timestamp | | `metrics` | Named range | Key metrics | | `data` | Sheet data | Full data export | --- ## Ticket Resolution **Best For:** Support desks (Zendesk, Freshdesk, Intercom) **Trigger Events:** - Ticket solved - Conversation closed **Attachment Type:** PDF (Transcript) ### Variable Mapping (Zendesk) | Template Variable | Zendesk Field | Description | |-------------------|---------------|-------------| | `customer.name` | `{{Ticket: Requester Name}}` | Customer name | | `customer.email` | `{{Ticket: Requester Email}}` | Customer email | | `ticket.id` | `{{Ticket: ID}}` | Ticket number | | `ticket.subject` | `{{Ticket: Subject}}` | Ticket subject | | `ticket.status` | `resolved` | Status (static) | | `ticket.createdAt` | `{{Ticket: Created At}}` | Creation date | | `ticket.resolvedAt` | `{{Ticket: Updated At}}` | Resolution date | | `agent.name` | `{{Ticket: Assignee Name}}` | Support agent | | `messages` | `{{Ticket: Comments}}` | Message thread | --- ## Lead Magnet Delivery **Best For:** Marketing forms (Typeform, Google Forms, Facebook Leads) **Trigger Events:** - Form submitted - Lead captured **Attachment Type:** PDF (Lead magnet document) ### Variable Mapping (Typeform) | Template Variable | Typeform Field | Description | |-------------------|----------------|-------------| | `lead.name` | `{{Answer: Name}}` | Lead's name | | `lead.email` | `{{Answer: Email}}` | Lead's email | | `lead.company` | `{{Answer: Company}}` | Company (if asked) | | `resource.title` | Static | Lead magnet title | | `resource.description` | Static | Description | | `nextSteps` | Static | Array of next actions | --- ## Meeting Confirmation **Best For:** Scheduling tools (Calendly, HubSpot Meetings) **Trigger Events:** - Meeting booked - Invitee created **Attachment Type:** PDF (Agenda) ### Variable Mapping (Calendly) | Template Variable | Calendly Field | Description | |-------------------|----------------|-------------| | `host.name` | `{{Event: Owner Name}}` | Host name | | `host.email` | `{{Event: Owner Email}}` | Host email | | `attendee.name` | `{{Invitee: Name}}` | Attendee name | | `attendee.email` | `{{Invitee: Email}}` | Attendee email | | `meeting.title` | `{{Event: Type Name}}` | Meeting type | | `meeting.startTime` | `{{Event: Start Time}}` | Start time | | `meeting.endTime` | `{{Event: End Time}}` | End time | | `meeting.timezone` | `{{Invitee: Timezone}}` | Timezone | | `meeting.location` | `{{Event: Location}}` | Location/video link | --- ## Best Practices ### 1. Test with Sample Data Always test your Zap with realistic data before going live: ``` 1. Use Zapier's "Test" feature 2. Verify email renders correctly 3. Check PDF/Excel attachment 4. Confirm all variables populate ``` ### 2. Handle Missing Data Use Zapier's Formatter to provide defaults: ``` {{Contact: Company}} or "Valued Customer" ``` ### 3. Format Currency and Dates Use Zapier's Formatter app to: - Convert cents to dollars (÷ 100) - Format dates (November 27, 2025) - Add currency symbols ($99.00) ### 4. Use Pre-Formatted Fields Our starter packs include pre-formatted fields in sample data: - `priceFormatted` instead of `price` - `totalFormatted` instead of `total` - `dateFormatted` instead of raw dates Map to these fields for cleaner output. --- ## Troubleshooting ### Attachment Not Generating - Verify PDF/Excel template ID is correct - Check all required variables are mapped - Ensure array data is properly formatted ### Variables Not Populating - Confirm variable names match exactly (case-sensitive) - Check for typos in `{{variable}}` syntax - Verify trigger data contains expected fields ### Formatting Issues - Use pre-formatted fields when available - Apply Zapier Formatter for custom formatting - Test with edge cases (long text, special characters) --- ## Next Steps --- # FormaMail Documentation Source: /

Send Email + PDF in One API Call

Stop stitching together SendGrid and DocRaptor.
Create email and PDF templates. Send with dynamically generated attachments in one API call.
```javascript // The FormaMail way: Email + PDF Invoice in one call const response = await fetch('https://api.formamail.com/api/emails/send', { method: 'POST', headers: { 'Authorization': 'Bearer your-api-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: "order-confirmation", to: [{ email: "customer@example.com", name: "John Doe" }], variables: { orderId: "12345", customerName: "John Doe", items: [{ name: "Widget", qty: 2, price: 29.99 }], total: 59.98 }, attachments: [{ attachmentTemplateId: "invoice-pdf" }] }) }); // That's it. Email sent with PDF invoice attached. ```

Why FormaMail?

1

One API Call

Send email + generate PDF + attach file. All in a single POST request.
1

Same Visual Designer

Design email, PDF, and Excel templates with the same intuitive drag-and-drop editor.
1

Visual Designer

Design email and PDF templates in the same drag-and-drop editor.

Explore Documentation

[Getting Started](/getting-started) - Your first email in 5 minutes
[Generate with AI](/tutorials/ai-template-generation) - Use ChatGPT/Claude
[Sending with Attachments](/developer-guide/sending-with-attachments) - Email + PDF/Excel
[Template Designer](/user-guide/email-designer) - Visual editor guide
[All Tutorials](/tutorials) - Step-by-step guides
[Integrations](/integrations) - OAuth, Webhooks, and more
[API Reference](/api-reference) - Complete endpoint documentation
[Security & Compliance](/security) - SOC2, GDPR, data handling

Need Help?

Email Support
support@formamail.com
FAQ
[Frequently Asked Questions](/faq)
Changelog
[What's New](/changelog)
--- # Limits & Quotas Source: /limits # Limits & Quotas This page documents all rate limits, payload limits, and quotas in FormaMail. We believe in transparency - knowing your limits before you hit them. ## API Rate Limits Rate limits protect the platform and ensure fair usage. Limits are applied per API key. ### Email Endpoints | Endpoint | Method | Limit | Window | |----------|--------|-------|--------| | `/api/emails/send` | POST | 30 requests | per minute | | `/api/emails/send/bulk` | POST | 5 requests | per minute | | `/api/emails` | GET | 300 requests | per minute | | `/api/emails/:id` | GET | 300 requests | per minute | | `/api/emails/:id/retry` | POST | 10 requests | per minute | ### Template Endpoints | Endpoint | Method | Limit | Window | |----------|--------|-------|--------| | `/api/templates` | GET | 300 requests | per minute | | `/api/templates` | POST | 30 requests | per minute | | `/api/templates/:id` | PATCH | 30 requests | per minute | | `/api/templates/:id` | DELETE | 30 requests | per minute | ### Analytics Endpoints | Endpoint | Method | Limit | Window | |----------|--------|-------|--------| | `/api/analytics/*` | GET | 100 requests | per minute | | `/api/dashboard/*` | GET | 100 requests | per minute | ### Webhook Subscription Endpoints | Endpoint | Method | Limit | Window | |----------|--------|-------|--------| | `/api/v1/webhook-subscriptions` | GET/POST/PATCH/DELETE | 30 requests | per minute | | `/api/v1/webhook-subscriptions/:id/test` | POST | 10 requests | per minute | ### Rate Limit Headers Every API response includes rate limit headers: ``` X-RateLimit-Limit: 30 X-RateLimit-Remaining: 25 X-RateLimit-Reset: 1732723200 ``` | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Maximum requests allowed in the window | | `X-RateLimit-Remaining` | Remaining requests in current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | ### Rate Limit Exceeded Response When you exceed the rate limit: ```json { "statusCode": 429, "code": "ERR_QUOTA_001", "message": "Rate limit exceeded. Please retry after 45 seconds.", "retryAfter": 45 } ``` **Best Practice**: Implement exponential backoff when you receive a 429 response. ## Payload Limits ### Request Limits | Limit | Value | Notes | |-------|-------|-------| | Request body size | 10 MB | JSON payload maximum | | URL length | 8 KB | Including query parameters | | Header size | 16 KB | Total header size | ### Email Limits | Limit | Value | Notes | |-------|-------|-------| | Recipients per email | 50 | Use bulk endpoint for more | | Attachments per email | 10 | Files or generated | | Total attachment size | 25 MB | Combined size | | Single attachment | 10 MB | Per file | | Email body (HTML) | 1 MB | Rendered template | | Subject line | 998 characters | RFC 2822 limit | ### Bulk Send Limits | Limit | Value | Notes | |-------|-------|-------| | Recipients per request | 1,000 | Batch size | | Unique emails per batch | 1,000 | De-duplicated | | Variables size per recipient | 100 KB | JSON per recipient | ## Attachment Generation Limits FormaMail enforces **"Safe Harbor" limits** on attachment generation to protect system resources and ensure reliable email delivery via AWS SES. ### PDF Limits | Limit | Value | Why | |-------|-------|-----| | **Pages per PDF** | 30 | Protects CPU/compute costs | | **Images per PDF** | 50 | Protects memory usage | | **Output file size** | 7 MB | Guarantees AWS SES delivery | | Page size | A4 / Letter | Standard sizes | | Generation timeout | 60 seconds | Per document | ### Excel Limits | Limit | Value | Why | |-------|-------|-----| | **Rows per workbook** | 10,000 | Protects memory/RAM | | **Columns per sheet** | 50 | Protects file size | | **Sheets per workbook** | 10 | Maximum sheets | | **Output file size** | 7 MB | Guarantees AWS SES delivery | | Cell content | 32,767 chars | Excel standard limit | | Generation timeout | 120 seconds | Per workbook | ### Template Processing Limits | Limit | Value | Why | |-------|-------|-----| | **Loop iterations** | 1,000 | Prevents hang-ups from large arrays | | **Nesting depth** | 10 levels | Prevents infinite recursion | | **Components per template** | 500 | Protects rendering performance | ### Limit Violation Errors When you exceed attachment limits, FormaMail immediately stops processing and returns a `400 Bad Request` with a specific error code: | Error Code | Limit | Example Message | |------------|-------|-----------------| | `ERR_LIMIT_001` | PDF pages | "PDF generation exceeded 30 page limit. Your PDF has 45 pages." | | `ERR_LIMIT_002` | PDF images | "PDF contains 75 images, exceeding the 50 image limit." | | `ERR_LIMIT_003` | Excel rows | "Excel generation exceeded 10,000 row limit. Your data has 15,000 rows." | | `ERR_LIMIT_004` | Excel sheets | "Excel workbook exceeded 10 sheet limit. Your workbook has 12 sheets." | | `ERR_LIMIT_005` | Excel columns | "Excel sheet exceeded 50 column limit. Your sheet has 75 columns." | | `ERR_LIMIT_006` | File size | "Generated attachment (7.5MB) exceeds safe email limit of 7MB." | | `ERR_LIMIT_007` | Loop iterations | "Loop \"items\" has 2,500 items but maximum is 1,000. Consider pagination or splitting data." | | `ERR_LIMIT_008` | Nesting depth | "Template exceeded 10 level nesting depth. Current depth: 12 levels." | | `ERR_LIMIT_009` | Component count | "Template exceeded 500 component limit. Your template has 650 components." | ### Example Error Response ```json { "statusCode": 400, "code": "ERR_LIMIT_001", "message": "PDF generation exceeded maximum page limit", "timestamp": "2025-12-09T10:30:00.000Z", "path": "/api/attachments/generate", "relatedInfo": { "actualPages": 45, "maxPages": 30, "message": "PDF generation exceeded 30 page limit. Your PDF has 45 pages." } } ``` ### How to Handle Limit Errors 1. **Split large datasets**: Break data into multiple attachments 2. **Paginate loops**: Use pagination for arrays over 1,000 items 3. **Optimize images**: Compress images before including in PDFs 4. **Simplify templates**: Reduce nesting and component count ## Template Limits ### Content Limits | Limit | Value | |-------|-------| | Variables per template | 500 | | Components per template | 200 | | Nested loop depth | 3 levels | | Conditional nesting | 5 levels | | Formula complexity | 1,000 operations | | Template name | 255 characters | | Template slug | 100 characters | ### Template Counts by Plan | Plan | Email Templates | Attachment Templates | |------|-----------------|---------------------| | Free | 5 | 2 | | Starter | 25 | 10 | | Pro | Unlimited | Unlimited | | Enterprise | Unlimited | Unlimited | ## Account Quotas ### Email Quotas by Plan | Plan | Emails/Month | Overage | |------|--------------|---------| | Free | 1,000 | Not available | | Starter | 50,000 | $0.001/email | | Pro | 200,000 | $0.0008/email | | Enterprise | Custom | Custom | ### Credit System FormaMail uses a credit-based system: - **1 email = 1 credit** - **1 PDF attachment = 1 credit** - **1 Excel attachment = 1 credit** Example: Sending an email with a PDF attachment uses 2 credits. ### Team Limits | Plan | Team Members | Teams | |------|--------------|-------| | Free | 1 | 1 | | Starter | 5 | 1 | | Pro | 10 | 3 | | Enterprise | Unlimited | Unlimited | ### API Key Limits | Plan | API Keys | |------|----------| | Free | 2 | | Starter | 10 | | Pro | 25 | | Enterprise | Unlimited | ## Webhook Limits ### Subscription Limits | Plan | Webhook Subscriptions | |------|----------------------| | Free | 2 | | Starter | 10 | | Pro | 25 | | Enterprise | Unlimited | ### Delivery Limits | Limit | Value | Notes | |-------|-------|-------| | Timeout | 30 seconds | Per delivery attempt | | Retries | 3 attempts | Exponential backoff | | Payload size | 256 KB | Maximum webhook payload | | Events per second | 100 | Per subscription | ## OAuth Limits ### Token Limits | Limit | Value | |-------|-------| | Access token lifetime | 1 hour | | Refresh token lifetime | 30 days | | Authorization code lifetime | 10 minutes | | Active access tokens per user | 50 | | Active refresh tokens per user | 10 | ### OAuth App Limits | Plan | OAuth Apps | |------|-----------| | Free | 1 | | Starter | 5 | | Pro | 10 | | Enterprise | Unlimited | ## Storage Limits ### Asset Storage | Plan | Storage | File Size | |------|---------|-----------| | Free | 100 MB | 5 MB/file | | Starter | 1 GB | 10 MB/file | | Pro | 10 GB | 25 MB/file | | Enterprise | Custom | Custom | ### Generated File Retention | File Type | Retention | |-----------|-----------| | PDF attachments | 7 days | | Excel attachments | 7 days | | Temporary files | 24 hours | ## Requesting Limit Increases If you need higher limits, you have options: ### Upgrade Your Plan Higher plans come with increased limits. See [Pricing](https://formamail.com/pricing). ### Enterprise Custom Limits Enterprise customers can request custom limits: - Dedicated infrastructure - Custom rate limits - Higher storage quotas - Dedicated IP addresses Contact sales@formamail.com ### Temporary Increases For one-time events (product launches, campaigns), request a temporary limit increase: **Email**: support@formamail.com **Include**: - Account/Team ID - Requested limit increase - Duration needed - Use case description ## Monitoring Your Usage ### Dashboard View your current usage in the dashboard: - **Overview**: Current email/credit usage - **Settings → Usage**: Detailed usage breakdown - **Settings → API Keys**: Per-key usage statistics ### API Query usage programmatically: ```bash GET /api/emails/usage/stats ``` Response: ```json { "period": "2024-11", "emailsSent": 15420, "creditsUsed": 18500, "creditsRemaining": 31500, "quota": 50000 } ``` ### Alerts Set up usage alerts in **Settings → Notifications**: - 50% quota usage - 80% quota usage - 100% quota usage - Rate limit warnings ## Best Practices ### Avoid Rate Limits 1. **Batch operations**: Use bulk endpoints instead of multiple single requests 2. **Cache responses**: Don't re-fetch unchanged data 3. **Implement backoff**: Exponential backoff on 429 responses 4. **Use webhooks**: Don't poll for status updates ### Optimize Payload Size 1. **Compress images**: Before including in templates 2. **Limit data**: Only include necessary variables 3. **Paginate queries**: Use pagination for large datasets ### Handle Limits Gracefully ```javascript async function sendWithRetry(payload, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { const response = await fetch('/api/emails/send', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify(payload) }); if (response.status === 429) { const retryAfter = response.headers.get('Retry-After') || 60; await sleep(retryAfter * 1000); continue; } return response.json(); } throw new Error('Max retries exceeded'); } ``` --- # Compliance & Certifications Source: /security/compliance # Compliance & Certifications FormaMail maintains compliance with major security and privacy frameworks to ensure your data is handled securely and in accordance with applicable regulations. ## SOC 2 Type II FormaMail is working toward SOC 2 Type II certification, which demonstrates our commitment to: - **Security**: Protection against unauthorized access - **Availability**: System uptime and performance monitoring - **Processing Integrity**: Accurate and timely data processing - **Confidentiality**: Protection of confidential information - **Privacy**: Personal information handling per privacy notice ### Request SOC 2 Report Once available, SOC 2 reports can be requested via our [contact page](https://www.formamail.com/contact). Reports are shared under NDA. ## GDPR Compliance FormaMail is fully GDPR (General Data Protection Regulation) compliant for EU customers and data subjects. ### Our GDPR Commitments | Requirement | How We Comply | |-------------|---------------| | **Lawful Basis** | We process data under contract (to provide the service) | | **Data Minimization** | We only collect data necessary for the service | | **Purpose Limitation** | Email data used only for sending/tracking emails | | **Storage Limitation** | Configurable retention periods (30-365 days) | | **Data Subject Rights** | Full support for access, deletion, portability | | **Data Protection Officer** | Available via [contact page](https://www.formamail.com/contact) | ### Data Subject Rights FormaMail supports all GDPR data subject rights: - **Right to Access**: Export all your data in JSON format via dashboard - **Right to Deletion**: Delete account and all associated data - **Right to Rectification**: Update personal data via dashboard - **Right to Data Portability**: Export data in machine-readable format - **Right to Object**: Opt out of marketing communications ### Data Processing Agreement (DPA) A DPA is available for all customers upon request. Our DPA covers: - Nature and purpose of processing - Types of personal data processed - Categories of data subjects - Duration of processing - Technical and organizational measures - Sub-processor list **Request a DPA**: Use our [contact page](https://www.formamail.com/contact) ### Sub-processors We use the following sub-processors to provide our services: | Sub-processor | Purpose | Location | |---------------|---------|----------| | Railway | Application hosting, database, cache | US | | Amazon Web Services (SES) | Email delivery | US | | Cloudflare | DNS, DDoS protection, file storage (R2) | US | | Vercel | Frontend hosting, CDN | US | | Cashfree | Payment processing | India | Sub-processor list is updated quarterly. Subscribe to updates via our [contact page](https://www.formamail.com/contact). ## CCPA Compliance FormaMail complies with the California Consumer Privacy Act (CCPA): ### Consumer Rights Under CCPA - **Right to Know**: Request what personal information we collect - **Right to Delete**: Request deletion of your personal information - **Right to Opt-Out**: We do not sell personal information - **Non-Discrimination**: No discrimination for exercising CCPA rights ### Do Not Sell My Personal Information **We do not sell personal information.** FormaMail does not sell, rent, or trade customer data to third parties for commercial purposes. ### CCPA Requests To exercise your CCPA rights, use the in-dashboard privacy center or contact us via our [contact page](https://www.formamail.com/contact). ## PCI DSS FormaMail does not directly process, store, or transmit credit card data. Payment processing is handled by **Cashfree**, which is PCI DSS Level 1 certified - the highest level of certification in the payment card industry. ### What This Means - Credit card numbers never touch our servers - Payment forms are hosted by Cashfree - We only store non-sensitive payment metadata (last 4 digits, expiry) - All payment data is handled in PCI-compliant environments ## HIPAA If you have HIPAA compliance requirements, please [contact us](https://www.formamail.com/contact) to discuss our roadmap. ## ISO 27001 ISO 27001 certification is on our roadmap for 2026. Our security practices already align with ISO 27001 principles: - Information security policies - Asset management - Access control - Cryptography - Operations security - Communications security - Incident management - Business continuity ## Data Residency ### Current Availability | Region | Status | Notes | |--------|--------|-------| | United States | Available | All customer data | | European Union | Not available | Data stored in US | | Asia Pacific | Not available | Data stored in US | ### Future Data Residency EU and other regional data residency options are on our roadmap for future consideration. [Contact us](https://www.formamail.com/contact) if you have specific requirements. ## Compliance Documentation ### Available Documents | Document | Availability | |----------|--------------| | Privacy Policy | [Public](/privacy) | | Terms of Service | [Public](/terms) | | Data Processing Agreement | On request | | SOC 2 Report | Coming Q2 2026 | | Penetration Test Summary | On request (Enterprise) | | Security Questionnaire | On request | ### Request Documents Use our [contact page](https://www.formamail.com/contact) to request compliance documentation. ## Compliance Contacts For all compliance-related inquiries including security, privacy, GDPR, legal, and contracts, please use our [contact page](https://www.formamail.com/contact). Select the appropriate subject when submitting your inquiry: - **Security** - For security issues or vulnerability reports - **Privacy** - For GDPR, CCPA, or other privacy-related requests - **Sales** - For enterprise compliance requirements or DPA requests - **Support** - For general compliance questions --- # Data Handling & Security Source: /security/data-handling # Data Handling & Security This page describes how FormaMail handles your data throughout its lifecycle - from collection to deletion. ## Security Measures ### Data in Transit All connections to FormaMail are secured via TLS through CloudFlare: | Protocol | Status | |----------|--------| | TLS 1.3 | Enabled (preferred) | | TLS 1.2 | Enabled (fallback) | | TLS 1.1 | Disabled | | TLS 1.0 | Disabled | | SSL | Disabled | **Additional protections:** - HSTS (HTTP Strict Transport Security) enforced via CloudFlare - DDoS protection via CloudFlare - Certificate transparency logging enabled ### Credential Security All sensitive credentials are hashed before storage: | Credential Type | Security Measure | |-----------------|------------------| | User passwords | bcrypt hashed (12 rounds) | | API keys | bcrypt hashed (never stored in plain text) | | OAuth access tokens | Hashed before storage | | OAuth refresh tokens | Hashed before storage | ### Infrastructure Security FormaMail uses a multi-cloud infrastructure with managed security: | Component | Provider | Security | |-----------|----------|----------| | Database (PostgreSQL) | Railway | Encrypted at rest (storage-level) | | Cache (Redis) | Railway | TLS connections | | Email Delivery | AWS SES | TLS, SPF/DKIM/DMARC | | CDN/WAF | CloudFlare | TLS 1.3, DDoS protection | ### API Key Security API keys are handled securely: - Keys are generated using cryptographically secure random number generators - Only the first and last 4 characters are displayed in the dashboard - Keys are hashed before storage (you cannot retrieve the full key after creation) - Keys can be scoped to specific permissions ## Data Retention FormaMail implements configurable data retention to balance functionality with data minimization: ### Default Retention Periods | Data Type | Default Retention | Configurable Range | |-----------|-------------------|-------------------| | Email logs | 90 days | 30-365 days | | Email content | 90 days | 30-365 days | | Generated PDFs/Excel | 7 days | 1-30 days | | Analytics data | 1 year | Not configurable | | Audit logs | 2 years | Not configurable | | Soft-deleted records | 30 days | Not configurable | ### Configuring Retention Retention periods can be configured in the dashboard under **Settings** → **Data Retention**. ### What Happens at Expiration When data reaches its retention limit: 1. **Soft Delete**: Data is marked as deleted but retained for 30 days 2. **Hard Delete**: Data is permanently removed from primary storage 3. **Backup Purge**: Backups containing the data expire per backup schedule ## Data Deletion ### Automatic Deletion FormaMail automatically deletes: - Generated attachments after 7 days - Soft-deleted records after 30 days - Email logs after configured retention period - Expired OAuth tokens and authorization codes ### Manual Deletion You can manually delete data via: **API:** ```bash # Delete a specific email log DELETE /api/emails/:id # Bulk delete email logs DELETE /api/emails/bulk ``` **Dashboard:** - Go to **Email Logs** → Select emails → **Delete** - Go to **Templates** → Select template → **Delete** ### Account Deletion To delete your entire account and all associated data: 1. Go to **Settings** → **Account** → **Delete Account** 2. Confirm your identity 3. Enter "DELETE" to confirm 4. All data is scheduled for deletion within 30 days ## Access Controls ### Role-Based Access Control (RBAC) FormaMail implements RBAC at the team level: | Role | Permissions | |------|-------------| | **Owner** | Full access, billing, team deletion | | **Admin** | Manage members, API keys, templates | | **Member** | Create/edit templates, send emails | | **Viewer** | Read-only access to logs and analytics | ### API Key Permissions API keys can be scoped to specific permissions: - `emails:send` - Send emails - `emails:read` - Read email logs - `templates:read` - Read templates - `templates:write` - Create/edit templates - `analytics:read` - Access analytics - `webhooks:manage` - Manage webhook subscriptions ### OAuth Scopes Third-party applications access your data through OAuth with explicit scopes: | Scope | Access Granted | |-------|---------------| | `emails:send` | Send emails on your behalf | | `emails:read` | Read your email logs | | `templates:read` | Read your templates | | `templates:write` | Create and modify templates | | `webhooks:read` | Read webhook configurations | | `webhooks:write` | Manage webhook subscriptions | | `analytics:read` | Read analytics data | | `profile:read` | Read basic profile information | ## Audit Logging FormaMail maintains audit logs for security-sensitive operations: ### Logged Events | Event Category | Examples | |----------------|----------| | Authentication | Login, logout, password change, 2FA enable/disable | | API Keys | Create, revoke, update permissions | | Team Management | Member invite, role change, removal | | Data Access | Template export, email log export | | OAuth | App authorization, token revocation | | Settings | Retention changes, billing updates | ### Accessing Audit Logs Audit logs are available to team Owners and Admins: - **Dashboard**: Settings → Activity Log - **API**: `GET /api/teams/:teamId/activity` ### Log Retention Audit logs are retained for 2 years and cannot be deleted manually. This ensures accountability and supports incident investigation. ## Data Processing ### Where Data is Processed | Process | Provider | Region | |---------|----------|--------| | API requests | Railway | US | | Database | Railway | US | | Email sending | AWS SES | eu-north-1 (Stockholm) | | PDF generation | Railway | US | ### Data Flow 1. **API Request**: Your request arrives via HTTPS at our API servers 2. **Validation**: Input is validated and sanitized 3. **Processing**: Email is queued, attachments generated 4. **Delivery**: Email sent via AWS SES 5. **Logging**: Delivery status logged for tracking 6. **Cleanup**: Temporary files deleted after delivery ## Backups & Recovery ### Backup Schedule | Backup Type | Frequency | Retention | |-------------|-----------|-----------| | Database snapshots | Daily | 30 days | | Point-in-time recovery | Continuous | 35 days | | Configuration backups | Weekly | 90 days | ### Disaster Recovery - **RTO (Recovery Time Objective)**: < 4 hours - **RPO (Recovery Point Objective)**: < 1 hour - **Infrastructure**: Railway managed PostgreSQL with automated backups ### Data Recovery Requests If you accidentally delete data, contact support@formamail.com within the retention period. Recovery may be possible from backups depending on timing. ## Third-Party Data Sharing ### We Do Not Sell Data FormaMail does not sell, rent, or trade your data to third parties. ### Service Providers We share data with service providers only as necessary to operate the service: | Provider | Purpose | Data Shared | |----------|---------|-------------| | Railway | Infrastructure (DB, API) | All data (encrypted at rest) | | AWS SES | Email delivery | Email content, recipients | | CloudFlare | CDN, WAF, DNS | Request metadata | | Cashfree | Payments | Billing info (not stored by us) | ### Legal Requirements We may disclose data if required by law, court order, or to protect our rights. We will notify you unless legally prohibited. ## Security Best Practices for Users ### Recommended Practices 1. **Rotate API keys regularly** (at least every 90 days) 2. **Use scoped API keys** with minimum required permissions 3. **Enable 2FA** for dashboard access 4. **Review connected OAuth apps** periodically 5. **Monitor your email logs** for unusual activity 6. **Use strong passwords** (16+ characters, unique) ### What to Do If Compromised If you suspect your account or API key is compromised: 1. **Immediately revoke** the compromised API key 2. **Change your password** 3. **Revoke all OAuth authorizations** 4. **Review recent activity** in audit logs 5. **Contact support** at security@formamail.com --- # Security & Compliance Source: /security # Security & Compliance FormaMail is built with enterprise-grade security. We understand that your transactional emails contain sensitive business data, and we take protecting that data seriously. ## Security Overview
TLS 1.3
All API traffic encrypted in transit
bcrypt
Passwords & keys hashed
GDPR
EU data protection compliant
99.9%
Uptime SLA target
## Quick Links - [Compliance & Certifications](/security/compliance) - SOC 2, GDPR, CCPA, PCI DSS - [Data Handling & Encryption](/security/data-handling) - How we protect your data - [Infrastructure & Reliability](/security/infrastructure) - AWS, uptime, disaster recovery ## Security Principles ### Defense in Depth We implement multiple layers of security: 1. **Network Layer**: WAF, DDoS protection, IP whitelisting (Enterprise) 2. **Application Layer**: Input validation, rate limiting, CSRF protection 3. **Data Layer**: TLS in transit, hashed credentials, access controls 4. **Operational Layer**: Audit logging, monitoring, incident response ### Least Privilege Access - API keys scoped to specific permissions - Role-based access control (Owner, Admin, Member, Viewer) - OAuth scopes limit third-party access - No shared credentials ### Data Minimization - We only store data necessary for the service - Email content retained for configurable period (default 90 days) - Generated attachments auto-deleted after 7 days - Logs retained for compliance then purged ## Authentication Methods FormaMail supports multiple secure authentication methods: | Method | Use Case | Security Level | |--------|----------|----------------| | API Keys | Server-to-server | High (scoped permissions) | | JWT Tokens | Dashboard/web apps | High (short-lived) | | OAuth 2.0 | Third-party integrations | High (scoped, revocable) | | 2FA | Dashboard login | Additional layer | ## Reporting Security Issues If you discover a security vulnerability, please report it responsibly: **Email**: security@formamail.com **Please include**: - Description of the vulnerability - Steps to reproduce - Potential impact - Your contact information We will acknowledge receipt within 24 hours and provide updates as we investigate. ## Security FAQ ### Is my data protected? Yes, we implement multiple security measures: - **In transit**: All connections secured via TLS (CloudFlare) - **Credentials**: Passwords, API keys, and OAuth tokens are hashed with bcrypt - **Email content**: Processed in real-time and never stored ### Where is my data stored? FormaMail infrastructure runs on AWS in the US-East-1 region. EU data residency options are planned for Q2 2026. ### Can I get a Data Processing Agreement (DPA)? Yes, DPAs are available for all customers. Contact support@formamail.com to request one. ### Do you process credit card data? No, we never handle or store credit card data directly. Payment processing is handled by Cashfree, a PCI DSS Level 1 certified provider. ### How do I report a security issue? Email security@formamail.com with details of the vulnerability. We take all reports seriously and will respond within 24 hours. ## Next Steps - [View compliance certifications](/security/compliance) - [Learn about data handling](/security/data-handling) - [Explore our infrastructure](/security/infrastructure) --- # Infrastructure & Reliability Source: /security/infrastructure # Infrastructure & Reliability FormaMail is built on modern, scalable cloud infrastructure designed for reliability. ## Cloud Infrastructure FormaMail uses a multi-provider infrastructure for optimal performance and reliability: ### Core Services | Component | Provider | Purpose | |-----------|----------|---------| | API Servers | Railway | Application hosting | | Database | Railway PostgreSQL | Primary data store | | Cache & Queues | Railway Redis | Session, caching, job queues | | Email Delivery | AWS SES | Transactional email sending | | File Storage | Cloudflare R2 | Attachments, assets, generated files | | CDN | Vercel Edge Network | Static asset delivery, frontend hosting | | DNS | Cloudflare | Domain management, DDoS protection | ### Region | Resource | Region | Notes | |----------|--------|-------| | API & Database | US | All application services | | File Storage | US | Cloudflare R2 | | Email (SES) | us-east-1 | AWS SES region | ## High Availability ### Railway Deployment FormaMail runs on Railway's managed infrastructure: - **API Servers**: Containerized deployment with automatic restarts - **Database**: Managed PostgreSQL with daily backups - **Redis**: Managed Redis for caching and queues ### Auto-Scaling FormaMail can scale based on demand: - **API containers**: Scale based on traffic - **Queue workers**: Scale based on queue depth - **Database**: Vertical scaling available as needed ## Uptime Targets ### Availability Goals | Metric | Target | Notes | |--------|--------|-------| | API Availability | 99.9% | Best effort | | Email Delivery | 99.9% | Via AWS SES | | Dashboard | 99.5% | Via Vercel | ## Disaster Recovery ### Recovery Objectives | Metric | Target | Description | |--------|--------|-------------| | **RTO** | < 4 hours | Recovery Time Objective | | **RPO** | < 24 hours | Recovery Point Objective | ### Backup Strategy **Database Backups:** - Daily automated snapshots via Railway - Point-in-time recovery available - Backup retention as per Railway's policies **File Backups:** - Cloudflare R2 durability (99.999999999%) - Object versioning available ### Recovery Procedures | Scenario | Recovery Method | Expected Time | |----------|-----------------|---------------| | Container failure | Automatic restart | < 1 minute | | Database issue | Railway failover/restore | < 1 hour | | Complete failure | Manual recovery from backups | < 4 hours | ## Security Infrastructure ### Network Security - **HTTPS Only**: All traffic encrypted via TLS 1.2+ - **Cloudflare Protection**: DDoS mitigation and WAF - **Railway Isolation**: Private networking between services ### Incident Response 1. **Detection**: Error monitoring and alerts 2. **Triage**: Engineering team assessment 3. **Mitigation**: Immediate actions to restore service 4. **Communication**: Direct customer notification if needed 5. **Resolution**: Root cause fix 6. **Post-mortem**: Incident review and prevention ## Email Infrastructure ### AWS SES Configuration FormaMail uses AWS SES for reliable email delivery: - **Shared IP Pool**: Standard sending infrastructure - **Bounce/Complaint Handling**: Automatic suppression list updates - **Feedback Loops**: ISP complaint processing ### Deliverability | Metric | Target | |--------|--------| | Delivery Rate | > 99% | | Bounce Rate | < 2% | | Complaint Rate | < 0.1% | ### Email Authentication FormaMail emails are authenticated using: - **SPF**: Sender Policy Framework - **DKIM**: DomainKeys Identified Mail - **DMARC**: Domain-based Message Authentication ## Queue Infrastructure ### BullMQ + Redis Email processing uses BullMQ queues backed by Redis: **Queue Configuration:** - Concurrency: 5 jobs per worker - Retry: 3 attempts with exponential backoff - Job retention: 7 days (completed), 30 days (failed) **Priorities:** - HIGH (1): Transactional emails - NORMAL (5): Standard sends - LOW (10): Bulk emails ### Queue Monitoring Monitor queue health via: - **Dashboard**: Settings → Queue Statistics - **API**: `GET /api/emails/queue/stats` ## Maintenance Windows ### Planned Maintenance Planned maintenance is scheduled during low-traffic periods when possible: - **Notice**: Advance notification when feasible - **Impact**: Usually zero-downtime deployments ### Emergency Maintenance Emergency maintenance for critical security patches: - **Notice**: As much as possible - **Communication**: Direct notification to affected customers ## Performance ### API Response Times | Endpoint | Target P95 | |----------|-----------| | `POST /api/emails/send` | < 500ms | | `GET /api/emails` | < 200ms | | `GET /api/templates` | < 100ms | ### Email Processing | Metric | Target | |--------|--------| | Queue to Send | < 30 seconds (normal load) | | PDF Generation | < 10 seconds | | Excel Generation | < 15 seconds | ## Network Addresses ### API Endpoints | Environment | URL | |-------------|-----| | Production | `https://api.formamail.com` | ### Webhook Source IPs If you need to whitelist FormaMail's webhook source IPs, contact support@formamail.com for the current IP range. ## Compliance Infrastructure ### Logging - **Application Logs**: Railway logging (retention varies) - **Audit Logs**: Database storage ### Data Residency | Region | Status | Notes | |--------|--------|-------| | US | Active | All customer data | | EU | Not available | All data stored in US | ## Questions? For infrastructure questions, contact: - **Technical Support**: support@formamail.com - **Security Inquiries**: security@formamail.com --- # Edit Templates with AI Source: /tutorials/ai-template-editing # How to Edit FormaMail Templates with AI You can use AI tools to modify existing FormaMail templates based on your requirements. Whether you need to add new components, change styling, add conditional logic, or restructure your template, AI can help. ## Prerequisites 1. **An AI Chat Account:** ChatGPT Plus, Claude, Gemini Advanced, or similar. 2. **Your Existing Template:** Export both the Variables JSON and Template Schema from FormaMail. 3. **The FormaMail Schema Reference:** Use one of these options: - **[Template Schema Reference](/developer-guide/template-schema)** - Interactive documentation page - **[LLM-Optimized Docs](/llms-full.txt)** - Plain text file for AI tools that accept URLs --- ## How to Export Your Current Template Before asking AI to edit your template, you need to export both the variables and schema. ### Export Variables 1. Open your template in the **Template Designer** 2. Click **Manage Variables** in the toolbar 3. Switch to the **Raw JSON** tab 4. Copy the entire JSON array ### Export Schema 1. Click the **code icon** (``) in the toolbar to open Raw Schema Editor 2. Copy the entire JSON schema --- ## The Edit Prompt Template Copy this prompt and customize it for your specific edit request: ```text I am providing you with an existing FormaMail template that I need to modify. Please read the FormaMail schema documentation first to understand the valid structure. **EXISTING VARIABLES (Plain JSON Array):** ```json [PASTE YOUR VARIABLES JSON HERE] ``` **EXISTING TEMPLATE SCHEMA:** ```json [PASTE YOUR SCHEMA JSON HERE] ``` **MY EDIT REQUEST:** [DESCRIBE WHAT YOU WANT TO CHANGE] **STRICT REQUIREMENTS:** 1. **Variables Format:** - Output variables as a **PLAIN JSON ARRAY** `[...]`. - Do NOT wrap variables in an object like `{ "variables": [...] }`. - Preserve ALL existing variables unless explicitly asked to remove them. - Add `defaultValue` for any NEW variables. 2. **Schema Changes:** - Preserve existing component IDs unless they need to change. - Use descriptive, kebab-case IDs for new components. - Use `rich-text` for all text content (NOT the deprecated `text` component). 3. **Output Format:** - Provide EXACTLY two separate JSON code blocks: - Block 1: The Updated Variables JSON (Plain Array) - Block 2: The Updated Template JSON (Schema) - Clearly indicate what changed with comments or explanations. ``` --- ## Common Edit Requests Here are prompts for common template modifications: ### Add a New Section ```text Add a new section to my email template that displays a promotional banner. The banner should: - Have a colored background (#f0f9ff) - Display a heading "Special Offer!" - Show promotional text with a variable {{promoMessage}} - Include a CTA button linking to {{promoUrl}} ``` ### Add Conditional Content ```text Add conditional logic to show a "Past Due" warning banner only when the variable {{paymentStatus}} equals "overdue". The banner should: - Have a red background (#fef2f2) - Display warning text "Payment is past due" - Only appear when the condition is true ``` ### Add a Data Table ```text Add a table component to display line items from my {{items}} array variable. The table should have columns for: - Item Name (from item.name) - Quantity (from item.quantity) - Unit Price (from item.price, formatted as currency) - Total (from item.total, formatted as currency) Include a footer row showing the grand total from {{orderTotal}}. ``` ### Change Styling ```text Update the styling of my template: - Change the header background color from #ffffff to #1e40af - Make the header text white - Increase the padding around the main content to 40px - Add rounded corners (8px) to the main container ``` ### Add a Calculated Variable ```text Add a calculated variable called "subtotal" that: - Sums up all item.price * item.quantity from the {{items}} array - Uses the expression: items.reduce((sum, item) => sum + (item.price * item.quantity), 0) - Has type "number" and isCalculated: true ``` ### Add a Loop Component ```text Replace the static list with a loop component that iterates over {{features}} array. Each feature should display: - A checkmark icon (use a green checkmark emoji or HTML entity) - The feature text from {{feature.name}} - A description from {{feature.description}} ``` ### Add an Image ```text Add a company logo image at the top of the template: - Use the variable {{companyLogo}} for the image source - Set width to 150px - Center the image - Add 20px margin below ``` ### Convert Email to PDF ```text Convert this email template to a PDF attachment template: - Change from sections array to pages array - Add appropriate page settings (A4, portrait, 20mm margins) - Preserve all the content and styling - Set outputFormats to ["pdf"] ``` --- ## Merging Changes Back to FormaMail After the AI generates updated JSON: ### Step 1: Validate Variables 1. Open **Manage Variables** in the Template Designer 2. Switch to **Raw JSON** tab 3. Paste the updated variables array 4. Click **Validate** to check for errors 5. Review the changes - ensure no variables were accidentally removed 6. Click **Apply Changes** ### Step 2: Validate Schema 1. Open the **Raw Schema Editor** (`` icon) 2. Paste the updated schema JSON 3. Click **Validate** to check for errors 4. Review the visual preview to ensure it looks correct 5. Click **Apply Changes** ### Step 3: Test the Template 1. Click **Preview** to see the template with sample data 2. Test any conditional logic by changing variable values 3. Verify loops render correctly with array data 4. Send a test email to verify the final output --- ## Pro Tips for Better Edits | Tip | Example | |-----|---------| | **Be Specific** | Instead of "make it look better", say "add 16px padding and #f3f4f6 background" | | **Reference Component IDs** | "Update the component with id 'header-text' to use font-size 24px" | | **Preserve Structure** | Ask AI to "add to" rather than "replace" when you want incremental changes | | **Request Explanations** | Add "Explain what you changed and why" to understand the modifications | | **Iterate Incrementally** | Make one change at a time for complex templates | --- ## Troubleshooting --- ## See Also - [Generate Templates with AI](/tutorials/ai-template-generation) - Create new templates from scratch - [Template Schema Reference](/developer-guide/template-schema) - Complete JSON schema documentation - [Variables Guide](/user-guide/variables) - Understanding variables and data types - [Component Reference](/user-guide/components) - All available components --- # Generate Templates with AI Source: /tutorials/ai-template-generation # How to Generate FormaMail Templates with AI You can use AI tools (ChatGPT, Claude, Gemini, etc.) to automatically generate professional FormaMail templates. By providing the AI with our **Schema Reference**, it learns exactly how to structure the JSON, which components to use, and how to define dynamic variables. ## Prerequisites 1. **An AI Chat Account:** ChatGPT Plus, Claude, Gemini Advanced, or similar. 2. **The FormaMail Schema:** Use one of these options: - **[Template Schema Reference](/developer-guide/template-schema)** - Interactive documentation page - **[LLM-Optimized Docs](/llms-full.txt)** - Plain text file for AI tools that accept URLs --- ## Step-by-Step Guide ### Step 1: Upload the Schema Open a new chat with your AI. Either: - Upload the schema reference file directly, or - Paste the content from our [Template Schema Reference](/developer-guide/template-schema) page ### Step 2: Copy the "Magic Prompt" Copy the text block below. This prompt ensures the AI follows our specific JSON structure, particularly the **Variables Array** format and **Page/Section** logic. ```text I am providing you with the technical documentation for FormaMail templates. Please read this carefully to understand the JSON structure, component types, and validation rules. **YOUR GOAL:** Generate a valid **[Email / PDF / Excel]** template for: **[INSERT YOUR DESCRIPTION HERE, e.g., A Monthly Subscription Invoice]**. **STRICT REQUIREMENTS:** 1. **Variables (CRITICAL):** - Output the variables definition as a **PLAIN JSON ARRAY** `[...]`. - Do NOT wrap variables in an object like `{ "variables": [...] }`. - Include `defaultValue` for ALL variables (even required ones) so I can preview the template immediately. - Use the unified variable system: Input variables, Constants (`isCalculated: false`), and Calculated Variables (`isCalculated: true`) must all be in this single array. 2. **Template Structure:** - If generating an **Email**: Use the `sections` array at the root. - If generating a **PDF** or **Excel**: Use the `pages` array at the root. - Use descriptive, kebab-case Component IDs (e.g., `invoice-header`, `items-table`). 3. **Components:** - Use `rich-text` for all text content (the `text` component is DEPRECATED). - Use `table` for data-driven tables from array variables. - Use `columns` or `container` for layouts. - Use `loop` to iterate over arrays. - Use `conditional` for if/else logic. 4. **Output Format:** Provide EXACTLY two separate JSON code blocks: - Block 1: The Variables JSON (Plain Array). - Block 2: The Template JSON (Schema). ``` ### Step 3: Customize and Send Paste the prompt into your chat. **Before hitting send**, replace the bracketed text with your specific needs. **Example:** > "Generate a valid **PDF** template for **a Construction Project Budget that calculates the total cost automatically based on line items and a tax rate constant**." ### Step 4: Import to FormaMail The AI will generate two JSON blocks: 1. **Variables JSON:** Copy this into **Variables Manager → Raw JSON** tab, then click **Apply**. 2. **Template JSON:** Copy this into **Schema Editor → Raw JSON** tab, then click **Apply**. --- ## Pro Tips for Better Results | Tip | Example Prompt | |-----|----------------| | **Ask for Math Logic** | *"Create a calculated variable that multiplies `quantity` by `unitPrice` for each line item."* | | **Conditional Content** | *"Show a 'Past Due' banner only if `status` equals 'overdue'."* | | **Use System Constants** | *"Add a footer with `{{__CURRENT_YEAR__}}` and `{{__COMPANY_NAME__}}`."* | | **PDF vs Excel** | Just change `outputFormats` to `["excel"]` - the structure is identical. | | **Sample Data** | Since we require `defaultValue`, your template will preview immediately. | --- ## Troubleshooting --- ## See Also - [Template Schema Reference](/developer-guide/template-schema) - Complete JSON schema documentation - [Variables Guide](/user-guide/variables) - Understanding variables and data types - [Component Reference](/user-guide/components) - All available components --- # Tutorials Source: /tutorials # Tutorials Step-by-step tutorials to help you master FormaMail features. ## Getting Started Tutorials Learn the basics of FormaMail with these beginner-friendly tutorials: ### 🚀 **[Send Your First Email](/tutorials/send-first-email)** Learn how to send your first email using the FormaMail API in just 5 minutes. **Time**: 5 minutes **Difficulty**: Beginner --- ### 🎨 **[Create an Email Template](/tutorials/create-template)** Build a beautiful email template using the drag-and-drop designer. **Time**: 15 minutes **Difficulty**: Beginner --- ## Advanced Tutorials Take your FormaMail skills to the next level: ### 📄 **[Generate PDF Invoices](/tutorials/generate-pdf)** Create professional PDF invoices from templates with dynamic data. **Time**: 20 minutes **Difficulty**: Intermediate --- ### 📧 **[Send Bulk Personalized Emails](/tutorials/bulk-emails)** Send thousands of personalized emails efficiently using batch operations. **Time**: 25 minutes **Difficulty**: Intermediate --- ## Framework Integration Tutorials Integrate FormaMail with popular frameworks: ### ⚡ **[Integrate with Next.js](/tutorials/nextjs-integration)** Add email functionality to your Next.js application. **Time**: 30 minutes **Difficulty**: Intermediate --- ### ⚛️ **[Integrate with React](/tutorials/react-integration)** Build email features in your React app. **Time**: 25 minutes **Difficulty**: Intermediate --- ### 🟢 **[Integrate with Node.js](/tutorials/node-integration)** Use FormaMail in your Node.js backend services. **Time**: 20 minutes **Difficulty**: Intermediate --- ## Tutorial Format Each tutorial includes: - **Prerequisites** - What you need before starting - **Step-by-step instructions** - Clear, numbered steps - **Code examples** - Copy-paste ready code - **Screenshots** - Visual guides where helpful - **Troubleshooting** - Common issues and solutions - **Next steps** - What to learn next --- ## Need Help? If you get stuck on any tutorial: - 💬 Ask in our [Discord Community](https://discord.gg/formamail) - 📖 Check the [User Guide](/user-guide) or [Developer Guide](/developer-guide) - 📧 Email us: support@formamail.com --- **Ready to learn?** → Choose a tutorial from the sidebar! --- # Feedback Widget Source: /user-guide/feedback # Feedback Widget The Feedback Widget allows you to quickly submit bug reports, feature requests, or questions directly from the FormaMail dashboard. Your feedback helps us improve the platform. ## Finding the Feedback Button The feedback button is located in the header bar, between the notifications bell and the theme toggle: ``` [Notifications 🔔] [Feedback 💬] [Theme 🌙] [User Menu] ``` Click the **message icon** (💬) to open the feedback form. ## Types of Feedback ### Bug Reports Use bug reports when you encounter: - Error messages or crashes - Features not working as expected - Visual glitches or display issues - Performance problems **Tips for effective bug reports:** - Describe what you were doing when the issue occurred - Explain what you expected to happen vs. what actually happened - Include any error messages you saw ### Feature Requests Use feature requests to suggest: - New functionality you'd like to see - Improvements to existing features - Integration ideas - Workflow enhancements **Tips for effective feature requests:** - Explain the problem you're trying to solve - Describe how the feature would help your workflow - Provide examples if possible ### Questions Use questions when you need: - Help understanding a feature - Guidance on best practices - Clarification on how something works ## Submitting Feedback ## Automatic Context Capture To help us understand and address your feedback, the widget automatically captures: | Information | Purpose | |-------------|---------| | Current page URL | Helps us reproduce issues | | Browser type | Identifies browser-specific issues | | Screen size | Helps with responsive design issues | | Console errors | Provides technical debugging info | ## What Happens After Submission 1. **Instant notification**: Our team receives your feedback immediately via Discord 2. **Logged for tracking**: Your feedback is saved in our system for follow-up 3. **Review and action**: Our team reviews and prioritizes all feedback 4. **Updates**: Major feature requests and bug fixes are documented in our changelog ## Best Practices ### Do - Be specific and detailed in your descriptions - Include steps to reproduce for bugs - One issue per submission - Be constructive and descriptive ### Don't - Submit duplicate feedback for the same issue - Include sensitive information (passwords, API keys) - Use the feedback widget for support tickets requiring immediate response ## Need Immediate Help? For urgent issues or account-related problems, please contact our support team directly: - **Email**: support@formamail.com - **Documentation**: Browse our [FAQ](/faq) for common questions --- # User Guide Source: /user-guide # User Guide Welcome to the FormaMail User Guide! This comprehensive guide will help you understand and use every feature of the FormaMail platform. ## What You'll Find Here This user guide is organized into sections covering all major features of FormaMail: ### Core Features - **[Dashboard](/user-guide/dashboard)** - Overview of your email activity, analytics, and quick actions - **[Templates](/user-guide/templates)** - Manage your email and attachment templates - **[Email Designer](/user-guide/email-designer)** - Create beautiful email templates with drag-and-drop - **[Attachment Designer](/user-guide/attachment-designer)** - Design PDF and Excel attachments - **[Asset Gallery](/user-guide/asset-gallery)** - Upload and manage images, documents, and files for templates ### Email Management - **[Email Logs](/user-guide/email-logs)** - Track and monitor all sent emails - **[Attachment Logs](/user-guide/attachment-logs)** - View generated PDF and Excel attachments - **[Analytics](/user-guide/analytics)** - Detailed insights into email performance ### API & Integration - **[API Playground](/user-guide/api-playground)** - Test API endpoints interactively - **[API Keys](/user-guide/api-keys)** - Manage your API authentication keys ### Administration - **[Team Management](/user-guide/team-management)** - Invite members and manage roles - **[Settings](/user-guide/settings)** - Account, security, and workspace settings - **[Sender Addresses](/user-guide/sender-addresses)** - Manage verified email sender addresses - **[Suppression List](/user-guide/suppression-list)** - Block addresses from receiving emails - **[Billing & Usage](/user-guide/billing)** - Subscription plans and usage tracking --- ## Quick Start If you're new to FormaMail, we recommend starting with: 1. **[Getting Started Guide](/getting-started)** - Set up your account and send your first email 2. **[Dashboard](/user-guide/dashboard)** - Understand your dashboard overview 3. **[Templates](/user-guide/templates)** - Learn how to create and manage templates 4. **[Email Designer](/user-guide/email-designer)** - Master the template designer --- ## Getting Help - 📖 Browse the [Developer Guide](/developer-guide) for technical integration - 💻 Check the [API Reference](/api-reference) for detailed endpoint documentation - 📧 Contact support: support@formamail.com - 💬 Join our [Discord Community](https://discord.gg/formamail) --- **Ready to dive in?** → Choose a topic from the sidebar to get started! --- # Frequently Asked Questions Source: /faq # Frequently Asked Questions Find answers to common questions about FormaMail. ## General Questions ### What is FormaMail? FormaMail is a comprehensive B2B/B2C email delivery platform that allows you to send emails with dynamically generated PDF and Excel attachments in a single API call. It's perfect for transactional emails, invoices, reports, and automated notifications. ### How is FormaMail different from other email services? FormaMail's key differentiator is **single API delivery with dynamic attachments**. Create email and attachment templates using the same visual editor, then send emails with dynamically generated PDF and Excel attachments in one API call. No more stitching together multiple services. ### Is there a free tier? Yes! You get **1,000 free emails** when you sign up - no credit card required. This includes: - Full API access - Unlimited templates - PDF/Excel generation - Basic analytics Perfect for testing and getting started. --- ## Pricing & Billing ### How does pricing work? FormaMail uses a simple **block-based pricing model**: - **1 Block = 10,000 emails** - Credits never expire - Volume discounts for larger purchases | Blocks | Price/Block | Total Emails | |--------|-------------|--------------| | 1 | $5.00 | 10,000 | | 2-4 | $4.00 | 20,000-40,000 | | 5-9 | $3.50 | 50,000-90,000 | | 10-19 | $3.00 | 100,000-190,000 | | 20-49 | $2.50 | 200,000-490,000 | | 50+ | $2.25 | 500,000+ | ### Do credits expire? No! Your email credits never expire. Purchase them once and use whenever you need. ### Is there a subscription? No monthly subscription required! FormaMail uses pay-as-you-go pricing. Purchase credits when you need them - no commitments, no auto-renewals. ### What payment methods do you accept? We accept: - Credit cards (Visa, Mastercard, American Express) - Debit cards - UPI (India) - Net Banking (India) All payments are processed securely through Cashfree. ### Do you offer refunds? All purchases are final. Email credits are non-refundable once purchased. Since credits never expire, you can use them at your own pace. See our [Refund Policy](https://formamail.com/refund) for full details. --- ## Technical Questions ### What's the API rate limit? API rate limits are generous for all users: - **Standard**: 100 requests/minute - **Burst**: Higher limits available for batch operations Need higher limits? Contact us for enterprise solutions. ### What email provider do you use? We use **Amazon SES** (Simple Email Service) for reliable, scalable email delivery with 99.9% uptime. ### Can I use my own domain for sending? Yes! You can configure a custom sending domain: 1. Go to **Settings** → **Domains** 2. Add your domain 3. Configure DNS records (SPF, DKIM, DMARC) 4. Verify domain This improves deliverability and sender reputation. ### Do you support attachments? Yes! You can: - Upload files (PDF, images, etc.) up to 10MB - Generate PDFs from templates dynamically - Generate Excel files from templates - Attach multiple files per email ### What's your email deliverability rate? Our average deliverability rate is **99.2%**, thanks to: - Amazon SES infrastructure - Automatic bounce handling - Spam complaint management - SPF, DKIM, and DMARC support - IP reputation monitoring --- ## Templates & Design ### How many templates can I create? **Unlimited templates** - there's no limit on the number of templates you can create. ### Can I import existing templates? Yes! You can: - Import HTML templates - Convert HTML to our template format - Use our drag-and-drop designer - Import from other platforms (some manual work required) ### Do templates work on mobile? Yes! All templates generated by FormaMail are: - Fully responsive - Mobile-optimized - Tested on major email clients (Gmail, Outlook, Apple Mail, etc.) ### Can I preview templates before sending? Yes! You can preview: - Desktop view - Mobile view - Different email clients - With sample data Use the **Preview** button in the template editor. --- ## API & Integration ### Do you have an API? Yes! FormaMail has a comprehensive REST API for: - Sending emails - Managing templates - Generating attachments - Viewing analytics - Managing API keys Check our [API Reference](/api-reference) for details. ### Are there SDKs available? Official SDKs are available for: - **Node.js** - `npm install @formamail/sdk` - **Python** - `pip install formamail` - **PHP** - `composer require formamail/sdk` - **Ruby** - `gem install formamail` - **Go** - `go get github.com/formamail/go-sdk` See [Developer Guide](/developer-guide/sdks) for documentation. --- ## Security & Privacy ### Is my data secure? Yes! We take security seriously: - **Transport Security**: TLS via CloudFlare for all connections - **Credential Security**: bcrypt hashing for passwords and API keys - **Authentication**: JWT tokens, API keys with permissions - **Infrastructure**: Railway-managed encryption at rest (storage-level) - **Compliance**: GDPR ready (data export, deletion, retention features) - **Backups**: Daily automated backups ### Do you store email content? We store: - Email metadata (to/from, subject, status, timestamps) - Template content - Delivery logs We **do NOT** store: - Recipient email addresses long-term (deleted after 90 days) - Email body content (only stored temporarily for retry) - Personal data beyond what's necessary ### Is FormaMail GDPR compliant? Yes! We are fully GDPR compliant: - Data processing agreements available - Right to data export - Right to deletion - EU data residency (coming soon) ### Can I delete my data? Yes! You can: - Delete individual emails or templates - Export all your data (JSON format) - Delete your entire account Go to **Settings** → **Account** → **Delete Account**. --- ## Support & Help ### How do I get support? Multiple ways to get help: - **Email**: support@formamail.com (24-48 hour response) - **Discord**: [Join our community](https://discord.gg/formamail) (fastest) - **Documentation**: This site! - **Bug reports**: [GitHub Issues](https://github.com/your-org/email-platform/issues) ### Do you offer onboarding? Yes! All users get access to: - Email onboarding guide - Video tutorials - Template library - Sample code For enterprise customers: - Dedicated account manager - Custom onboarding call - Migration assistance - Technical implementation support ### Where can I see system status? Check our status page: [status.formamail.com](https://status.formamail.com) Subscribe for notifications about incidents and maintenance. --- ## Still have questions? - Join our [Discord Community](https://discord.gg/formamail) - Email us: support@formamail.com - Browse the [User Guide](/user-guide) or [Developer Guide](/developer-guide) - [Schedule a demo](https://formamail.com/demo) with our team --- **Can't find what you're looking for?** → [Contact Support](mailto:support@formamail.com) ---