Loopfour
IntegrationsCommunication

Email

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

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

Prerequisites

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

Authentication

Gmail / Outlook (OAuth)

# Gmail
curl "https://workflow.loopfour.ai/api/v1/connections/gmail/auth-url" \
  -H "x-api-key: YOUR_API_KEY"

# Outlook
curl "https://workflow.loopfour.ai/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.

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
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. The send_template action is accepted by the step schema, but Resend has no server-side template API, so the action fails at run time on every available provider with does not support server-side templates.

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": "resend",
    "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
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}}"
  }
}

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

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

On this page