When Lettr receives an email on your inbound domain, it automatically parses the raw email into a structured format. This page explains how different parts of the email are extracted and made available in the webhook payload.
Emails can contain plain text, HTML, or both. These are available in the content object:
const { text, html } = relay.content;// Use HTML if available, fall back to plain textconst displayContent = html || `<pre>${escapeHtml(text)}</pre>`;// Or prefer plain text for processingconst processableContent = text || stripHtml(html);
Lettr automatically handles character encoding conversion:
// These are all decoded to UTF-8:// - ISO-8859-1 encoded content// - Windows-1252 encoded content// - Base64 encoded UTF-8// - Quoted-Printable encoded contentconst { text, subject } = relay.content;// Both are UTF-8 strings regardless of original encoding
Emails with multiple parts (text + HTML + attachments) are automatically parsed:
// A multipart email:// - text/plain part → relay.content.text// - text/html part → relay.content.html// - Full raw MIME → relay.content.email_rfc822const hasRichContent = relay.content.html && relay.content.text;
The email_rfc822 field contains the complete raw MIME message, which you can parse with a MIME library if you need access to attachments or other parts not extracted into the structured fields.
For advanced use cases like extracting inline images, embedded attachments, or handling complex MIME structures, parse the raw email_rfc822 content with a MIME library:
import { simpleParser } from 'mailparser';async function parseRawEmail(relay) { const parsed = await simpleParser(relay.content.email_rfc822); return { from: parsed.from?.value, to: parsed.to?.value, subject: parsed.subject, text: parsed.text, html: parsed.html, attachments: parsed.attachments // includes inline images with cid };}
function extractSignature(text) { // Common signature delimiters const delimiters = [ /^--\s*$/m, // Standard delimiter /^_{3,}$/m, // Underscores /^-{3,}$/m, // Dashes /^Sent from my /m, // Mobile signatures /^Get Outlook for /m // Outlook mobile ]; let signatureStart = text.length; for (const delimiter of delimiters) { const match = text.match(delimiter); if (match && match.index < signatureStart) { signatureStart = match.index; } } return { body: text.substring(0, signatureStart).trim(), signature: text.substring(signatureStart).trim() };}
function parseEmailAddress(address) { if (!address) return null; // Handle various formats const match = address.match(/<([^>]+)>/) || // "Name <email>" address.match(/([^\s<>]+@[^\s<>]+)/); // bare email return match ? match[1].toLowerCase() : null;}