Skip to main content
Version: 3.0

Working with E-Mails

Reaktors can receive and reply to e-mails through the @operaide/mail package. When someone sends an e-mail to a configured address, Operaide parses the message and routes it to your Reaktor as structured input. Your Reaktor can then process the content, work with attachments, and send a reply — all within the same workflow.


Overview

PatternWhen to Use
Mail Reaktor (registerMailReaktorDefinition)Receive incoming e-mails as Reaktor input
Send (aktorSendMail)Send a new e-mail from within a Reaktor
Reply (aktorReplyToMail)Reply to an incoming e-mail while preserving the thread
Attachments (aktorMapFilesToBase64)Convert PDF attachments to base64 for further processing

All e-mail Aktors are imported from @operaide/mail.


Receiving E-Mails

Use registerMailReaktorDefinition to create a Reaktor that receives incoming e-mails. It is a convenience wrapper around registerReaktorDefinition that pre-configures the input schema to the ParsedMail type and disables chat mode.

import { createAktorComposition } from '@operaide/aktor';
import { registerMailReaktorDefinition } from '@operaide/mail';

const aktorMyMailHandler = createAktorComposition('aktorMyMailHandler', (params) => {
// params is a ParsedMail object
// ... your workflow logic here
});

registerMailReaktorDefinition({
reaktorDefinitionId: 'my-mail-handler',
label: 'My Mail Handler',
description: 'Processes incoming e-mails.',
aktor: aktorMyMailHandler,
});

The ParsedMail Input

When an e-mail arrives, the platform parses it and passes a ParsedMail object to your Aktor composition callback. The most important properties are:

PropertyTypeDescription
senderstringE-mail address of the sender
recipientstringE-mail address the message was sent to
fromstringFull "From" header (may include display name)
subjectstringSubject line
bodyPlainstringPlain-text body
bodyHtmlstring (optional)HTML body
messageIdstringUnique message ID — pass this to aktorReplyToMail for threading
messageHeadersstringRaw message headers (useful for extracting In-Reply-To)
filesMailAttachment[]Attached files (see Working with Attachments)
attachmentCountnumber (optional)Number of attachments

Sending E-Mails

Use aktorSendMail to send a new e-mail that is not a reply to an existing thread.

import { aktorSendMail } from '@operaide/mail';

const result = aktorSendMail({
from: 'noreply@example.com',
to: 'recipient@example.com',
subject: 'Your report is ready',
htmlBody: '<p>The report has been generated.</p>',
});
ParameterTypeDescription
fromstringSender e-mail address
tostringRecipient e-mail address
subjectstringSubject line
htmlBodystringHTML body of the e-mail

Replying to E-Mails

Use aktorReplyToMail to reply to an incoming e-mail. It accepts the same parameters as aktorSendMail plus a messageId that links the reply to the original thread. The subject is automatically prefixed with "Re: ".

import { aktorReplyToMail } from '@operaide/mail';

const reply = aktorReplyToMail({
from: params.recipient,
to: params.sender,
subject: params.subject,
messageId: params.messageId,
htmlBody: '<p>Thank you for your message.</p>',
});
ParameterTypeDescription
fromstringSender e-mail address (typically the original recipient)
tostringRecipient e-mail address (typically the original sender)
subjectstringOriginal subject — "Re: " is added automatically
messageIdstringThe messageId from the incoming ParsedMail
htmlBodystringHTML body of the reply
tip

Always pass the original messageId when replying. This ensures the reply appears in the same thread in the recipient's mail client.


Working with Attachments

The files array on ParsedMail contains MailAttachment objects with these properties:

PropertyTypeDescription
originalnamestringFile name
mimetypestringMIME type (e.g. application/pdf)
bufferBufferRaw file content
sizenumberFile size in bytes

Converting PDFs to Base64

Use aktorMapFilesToBase64 to extract PDF attachments as base64-encoded strings. It filters for application/pdf files and ignores other MIME types.

import { aktorMapFilesToBase64 } from '@operaide/mail';

const base64Pdfs = aktorMapFilesToBase64({ files: params.files });
// returns string[] — one base64 string per PDF attachment

To convert the PDFs to Markdown with Document Intelligence, use aktorConvertMultiplePDFsToMarkdown for the full array, or aktorConvertPDFToMarkdown if you only need a single file:

import { aktorConvertMultiplePDFsToMarkdown } from '@operaide/document';

const markdowns = aktorConvertMultiplePDFsToMarkdown({
pdfs: base64Pdfs,
connectionName: 'default',
});

Full Example: E-Mail to Data Extraction

This example receives an e-mail with a PDF attachment, converts it to Markdown, extracts structured data using AI, and replies with the result.

import { aktorJoinStrings, aktorSetting, createAktorComposition } from '@operaide/aktor';
import { aktorAICall, aktorAISettingProviderModel, aktorMessagesFromSystemAndUser } from '@operaide/ai';
import { aktorConvertMultiplePDFsToMarkdown } from '@operaide/document';
import { aktorMapFilesToBase64, aktorReplyToMail, registerMailReaktorDefinition } from '@operaide/mail';
import { z } from 'zod';

const aktorEmailDataExtractor = createAktorComposition('aktorEmailDataExtractor', (params) => {
// Convert PDF attachments to base64
const base64Pdfs = aktorMapFilesToBase64({ files: params.files });

// Convert to Markdown via Document Intelligence
const markdowns = aktorConvertMultiplePDFsToMarkdown({
pdfs: base64Pdfs,
connectionName: aktorSetting(z.string(), 'default', 'Document Intelligence Connection'),
});

// Join multiple markdowns into a single string
const document = aktorJoinStrings({ strings: markdowns });

// Extract structured data with AI
const extracted = aktorAICall({
messages: aktorMessagesFromSystemAndUser({
system: aktorSetting(
z.string().describe('[textarea] System Prompt'),
'Extract the order number, date, and line items from the document.',
'Extraction Prompt'
),
user: document,
}),
providerModel: aktorAISettingProviderModel(),
});

// Reply with the result
return aktorReplyToMail({
from: params.recipient,
to: params.sender,
subject: params.subject,
messageId: params.messageId,
htmlBody: extracted,
});
});

registerMailReaktorDefinition({
reaktorDefinitionId: 'email-data-extractor',
label: 'Email Data Extractor',
description: 'Extracts data from e-mail attachments and replies with the result.',
aktor: aktorEmailDataExtractor,
});

Best Practices

  1. Use registerMailReaktorDefinition instead of the generic registerReaktorDefinition — it pre-configures the ParsedMail input schema and disables chat mode for you.

  2. Always include messageId when replying so the response threads correctly in the recipient's mail client.

  3. Swap from and to when replying: set from to params.recipient (the address that received the original e-mail) and to to params.sender.

  4. Wrap runtime settings in aktorSetting (connection names, prompts, URLs) so operators can configure them at deployment time without code changes.

  5. Combine with Document Intelligence for PDF attachments — use aktorMapFilesToBase64 to get base64 data, then pass it to aktorConvertMultiplePDFsToMarkdown or aktorConvertPDFToMarkdown to produce Markdown that an AI model can process.