LoopFour
IntegrationsCommunication

Email

Multi-provider email integration for Gmail, Outlook, Resend, and SendGrid

Email

Send emails through multiple providers with unified configuration and Handlebars template support.

Overview

The Email integration is a building block that supports multiple email providers:

  • Gmail - Google Workspace email via OAuth
  • Outlook - Microsoft 365 email via OAuth
  • Resend - Developer-friendly email API
  • SendGrid - Transactional email platform

Prerequisites

ProviderRequirements
GmailGoogle Workspace account, OAuth connection via Nango
OutlookMicrosoft 365 account, OAuth connection via Nango
ResendResend account, API key
SendGridSendGrid account, API key connection via Nango

Authentication

Gmail / Outlook (OAuth)

# Gmail
curl "http://localhost:3000/api/v1/connections/google-mail/auth-url" \
  -H "x-api-key: YOUR_API_KEY"
 
# Outlook
curl "http://localhost:3000/api/v1/connections/microsoft-graph/auth-url" \
  -H "x-api-key: YOUR_API_KEY"

Resend (API Key)

Set the RESEND_API_KEY environment variable or pass api_key in the step config.

SendGrid (API Key via Nango)

curl "http://localhost:3000/api/v1/connections/sendgrid/auth-url" \
  -H "x-api-key: YOUR_API_KEY"

Email Step Configuration

The email step uses type email in workflow definitions:

{
  "id": "send-notification",
  "type": "email",
  "name": "Send Invoice Email",
  "config": {
    "action": "send",
    "provider": "gmail",
    "to": "{{input.customer_email}}",
    "subject": "Invoice #{{input.invoice_number}}",
    "body": {
      "html": "<p>Hello {{input.customer_name}},</p><p>Your invoice is ready.</p>"
    }
  }
}

Available Actions

send

Send an email immediately.

{
  "id": "send-email",
  "type": "email",
  "config": {
    "action": "send",
    "provider": "gmail",
    "to": "{{input.recipient}}",
    "cc": ["manager@company.com"],
    "bcc": ["archive@company.com"],
    "from": "notifications@company.com",
    "reply_to": "support@company.com",
    "subject": "Your Order #{{input.order_number}} Has Shipped",
    "body": {
      "html": "<h1>Order Shipped!</h1><p>Hi {{input.customer_name}},</p><p>Your order is on its way.</p>",
      "text": "Order Shipped! Hi {{input.customer_name}}, Your order is on its way."
    },
    "attachments": [
      {
        "filename": "shipping-label.pdf",
        "content": "{{input.label_base64}}",
        "content_type": "application/pdf"
      }
    ],
    "headers": {
      "X-Order-ID": "{{input.order_id}}"
    },
    "priority": "1"
  }
}

Parameters:

FieldTypeRequiredDescription
actionstringYesMust be send
providerstringYesgmail, outlook, resend, sendgrid
tostring/arrayYesRecipient email(s)
ccarrayNoCC recipients
bccarrayNoBCC recipients
fromstringNoSender address
reply_tostringNoReply-to address
subjectstringYesEmail subject (supports templates)
body.htmlstringNoHTML body (supports templates)
body.textstringNoPlain text body (supports templates)
attachmentsarrayNoFile attachments
headersobjectNoCustom email headers
prioritystringNoPriority (1=high, 3=normal, 5=low)
template_variablesobjectNoAdditional template variables

sendTemplate

Send using a server-side template (SendGrid only).

{
  "id": "send-template",
  "type": "email",
  "config": {
    "action": "send_template",
    "provider": "sendgrid",
    "to": "{{input.email}}",
    "from": "noreply@company.com",
    "template": {
      "id": "d-abc123def456",
      "variables": {
        "first_name": "{{input.first_name}}",
        "order_total": "{{input.total}}",
        "items": "{{input.line_items}}"
      }
    }
  }
}

Note: Gmail and Outlook do not support server-side templates. Use inline Handlebars templates with the send action instead.

schedule

Schedule an email for later delivery.

{
  "id": "schedule-reminder",
  "type": "email",
  "config": {
    "action": "schedule",
    "provider": "sendgrid",
    "to": "{{input.email}}",
    "subject": "Reminder: Your appointment tomorrow",
    "body": {
      "html": "<p>Hi {{input.name}}, this is a reminder about your appointment.</p>"
    },
    "send_at": "{{input.reminder_time}}"
  }
}

Scheduling Support:

ProviderNative Scheduling
SendGrid✅ Yes
Resend✅ Yes
Gmail❌ No (use Trigger.dev scheduled tasks)
Outlook❌ No (use Trigger.dev scheduled tasks)

Handlebars Templates

The email integration uses Handlebars for dynamic content:

Basic Variables

Hello {{input.customer_name}},
 
Your order #{{input.order_number}} has been confirmed.

Conditionals

{{#if input.is_premium}}
  <p>Thank you for being a Premium member!</p>
{{else}}
  <p>Upgrade to Premium for exclusive benefits.</p>
{{/if}}

Loops

<h2>Order Items:</h2>
<ul>
{{#each input.items}}
  <li>{{this.name}} - ${{this.price}} x {{this.quantity}}</li>
{{/each}}
</ul>

Nested Properties

Shipping to: {{input.shipping.address.city}}, {{input.shipping.address.state}}

Provider-Specific Features

Gmail

  • Uses RFC 2822 message format
  • Supports threading via in_reply_to and references
  • Full attachment support with base64 encoding
{
  "config": {
    "provider": "gmail",
    "in_reply_to": "{{input.original_message_id}}",
    "references": ["{{input.thread_message_id}}"]
  }
}

Outlook

  • Uses Microsoft Graph API
  • Saves sent emails to Sent Items
  • Supports importance levels
{
  "config": {
    "provider": "outlook",
    "priority": "1"
  }
}

Resend

  • Simple API-first design
  • Direct API calls (no Nango required)
  • Supports scheduling
{
  "config": {
    "provider": "resend",
    "api_key": "{{env.RESEND_API_KEY}}"
  }
}

SendGrid

  • Server-side templates with dynamic data
  • Open and click tracking
  • Scheduling support
{
  "config": {
    "provider": "sendgrid",
    "tracking": {
      "opens": true,
      "clicks": true
    }
  }
}

Example Workflow

Invoice email workflow:

{
  "name": "Send Invoice Email",
  "trigger": {
    "type": "webhook",
    "provider": "stripe",
    "events": ["invoice.finalized"]
  },
  "steps": [
    {
      "id": "get-customer",
      "type": "action",
      "action": "stripe.getCustomer",
      "config": {
        "customerId": "{{input.data.object.customer}}"
      }
    },
    {
      "id": "send-invoice-email",
      "type": "email",
      "name": "Email Invoice to Customer",
      "config": {
        "action": "send",
        "provider": "gmail",
        "to": "{{steps.get-customer.output.email}}",
        "subject": "Invoice #{{input.data.object.number}} from {{company.name}}",
        "body": {
          "html": "<h1>Invoice Ready</h1><p>Hi {{steps.get-customer.output.name}},</p><p>Your invoice for <strong>${{input.data.object.amount_due | divided_by: 100}}</strong> is ready.</p><p><a href=\"{{input.data.object.hosted_invoice_url}}\">View Invoice</a></p><p>Due date: {{input.data.object.due_date | date: 'MMMM D, YYYY'}}</p>"
        }
      }
    },
    {
      "id": "notify-finance",
      "type": "action",
      "action": "slack.sendMessage",
      "config": {
        "channel": "#finance",
        "text": "Invoice #{{input.data.object.number}} sent to {{steps.get-customer.output.email}}"
      }
    }
  ]
}

Attachments

Base64 Content

{
  "attachments": [
    {
      "filename": "report.pdf",
      "content": "{{steps.generate-pdf.output.base64}}",
      "content_type": "application/pdf"
    }
  ]
}

Multiple Attachments

{
  "attachments": [
    {
      "filename": "invoice.pdf",
      "content": "{{input.invoice_pdf}}",
      "content_type": "application/pdf"
    },
    {
      "filename": "terms.pdf",
      "content": "{{env.TERMS_PDF_BASE64}}",
      "content_type": "application/pdf"
    }
  ]
}

Rate Limits

ProviderLimit
Gmail2,000 messages/day (free), higher for Workspace
Outlook10,000 messages/day
ResendBased on plan
SendGridBased on plan

Troubleshooting

Common Errors

ErrorCauseSolution
connectionId is requiredNo OAuth connectionSet up connection via Nango
Resend API key not foundMissing API keySet RESEND_API_KEY env var
template is requiredUsing sendTemplate without templateProvide template.id
provider does not support server-side templatesGmail/Outlook with sendTemplateUse send action with inline templates
provider does not support native schedulingGmail/Outlook with scheduleUse Trigger.dev scheduled tasks

Template Rendering Issues

  • Ensure all template variables exist in the context
  • Use {{#if variable}} for optional fields
  • Check for typos in variable paths