Email Inbox

Aiviro provides a set of actions for working with emails — fetching, sending, managing messages, and organizing folders. All actions are stateless and support multiple authentication methods (IMAP basic, Outlook OAuth2, Exchangelib).

Actions

Action for fetching emails via IMAP.

class aiviro.actions.email.fetch.FetchEmailsAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], folder: str | None = None, limit: int | None = None, seen: bool | None = None)

Stateless action that fetches emails from a mailbox folder.

Parameters:
  • config – IMAP authentication configuration.

  • folder – Folder to fetch from. Defaults to the initial folder configured in the IMAP config (usually "INBOX").

  • limit – Maximum number of emails to fetch. None means no limit.

  • seen – If True, fetch only seen (read) emails. If False, fetch only unseen (unread) emails. If None, fetch all emails. Default None.

Example:

>>> import aiviro
>>> from aiviro.actions.email import FetchEmailsAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>>
>>> # Fetch the 5 newest unread emails from INBOX
>>> emails = FetchEmailsAction(config=config, limit=5, seen=False)(robot=r)
>>> for email in emails:
...     print(email.subject, email.sender.address)
>>>
>>> # Fetch all emails from a specific folder
>>> emails = FetchEmailsAction(config=config, folder="Archive/2024")(robot=r)

Action for sending emails via SMTP.

class aiviro.actions.email.send.SendEmailAction(config: Annotated[SMTPBasicConfig | SMTPOutlookOAuth2PublicConfig | SMTPOutlookOAuth2ConfidentialConfig | SMTPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], recipients: str | tuple[str, str] | list[str | tuple[str, str]], subject: str, message: str, attachments: Sequence[str | Path | EmailAttachment] | None = None, cc: str | tuple[str, str] | list[str | tuple[str, str]] | None = None, bcc: str | tuple[str, str] | list[str | tuple[str, str]] | None = None, reply_msg: EmailMessage | None = None, sender_email: str | None = None, message_as_html: bool = False)

Stateless action that sends an email via SMTP.

Parameters:
  • config – SMTP authentication configuration.

  • recipients – One or more recipients in format "email" or ("fullname", "email").

  • subject – Subject of the message.

  • message – Content of the message (plain text or HTML).

  • attachments – Optional list of file paths or EmailAttachment objects to attach.

  • cc – Carbon copy recipients, same format as recipients.

  • bcc – Blind carbon copy recipients, same format as recipients.

  • reply_msg – If this message is a reply, include the original EmailMessage to set proper headers.

  • sender_email – Override the sender address (e.g. shared mailbox).

  • message_as_html – If True, message is treated as HTML.

Example:

>>> import aiviro
>>> from aiviro.actions.email import SendEmailAction, SMTPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = SMTPBasicConfig(server="smtp.example.com", username="user", password="pass")
>>>
>>> # Send a simple plain-text email
>>> SendEmailAction(
...     config=config,
...     recipients="recipient@example.com",
...     subject="Hello",
...     message="This is the email body.",
... )(robot=r)
>>>
>>> # Send an HTML email with attachments and CC
>>> SendEmailAction(
...     config=config,
...     recipients=[("John Doe", "john@example.com"), "jane@example.com"],
...     subject="Report",
...     message="<h1>Monthly Report</h1><p>See attachment.</p>",
...     attachments=["path/to/report.pdf"],
...     cc="manager@example.com",
...     message_as_html=True,
... )(robot=r)

Actions for managing emails (move, copy, delete, set seen).

class aiviro.actions.email.manage.MoveEmailAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], message: EmailMessage, destination_folder: str)

Stateless action that moves an email to another folder.

Parameters:
  • config – IMAP authentication configuration.

  • message – Email message to move.

  • destination_folder – Target folder path. Defaults to "INBOX".

Example:

>>> import aiviro
>>> from aiviro.actions.email import FetchEmailsAction, MoveEmailAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> emails = FetchEmailsAction(config=config, limit=1, seen=False)(robot=r)
>>> MoveEmailAction(config=config, message=emails[0], destination_folder="Archive")(robot=r)
class aiviro.actions.email.manage.CopyEmailAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], message: EmailMessage, destination_folder: str)

Stateless action that copies an email to another folder.

Parameters:
  • config – IMAP authentication configuration.

  • message – Email message to copy.

  • destination_folder – Target folder path. Defaults to "INBOX".

Example:

>>> import aiviro
>>> from aiviro.actions.email import FetchEmailsAction, CopyEmailAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> emails = FetchEmailsAction(config=config, limit=1)(robot=r)
>>> CopyEmailAction(config=config, message=emails[0], destination_folder="Backup")(robot=r)
class aiviro.actions.email.manage.DeleteEmailAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], message: EmailMessage)

Stateless action that deletes an email.

Parameters:
  • config – IMAP authentication configuration.

  • message – Email message to delete. Defaults to "INBOX".

Example:

>>> import aiviro
>>> from aiviro.actions.email import FetchEmailsAction, DeleteEmailAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> emails = FetchEmailsAction(config=config, limit=1)(robot=r)
>>> DeleteEmailAction(config=config, message=emails[0])(robot=r)
class aiviro.actions.email.manage.SetEmailSeenAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], message: EmailMessage, seen: bool)

Stateless action that marks an email as seen or unseen.

Parameters:
  • config – IMAP authentication configuration.

  • message – Email message to mark.

  • seenTrue to mark as seen, False for unseen.

  • source_folder – Folder that contains the email. Defaults to "INBOX".

Example:

>>> import aiviro
>>> from aiviro.actions.email import FetchEmailsAction, SetEmailSeenAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> emails = FetchEmailsAction(config=config, limit=1, seen=False)(robot=r)
>>> # Mark the email as read
>>> SetEmailSeenAction(config=config, message=emails[0], seen=True)(robot=r)

Actions for managing email folders.

class aiviro.actions.email.folders.ListFoldersAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')])

Stateless action that lists all mailbox folders.

Parameters:

config – IMAP authentication configuration.

Example:

>>> import aiviro
>>> from aiviro.actions.email import ListFoldersAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> folders = ListFoldersAction(config=config)(robot=r)
>>> for folder in folders:
...     print(folder.name)
class aiviro.actions.email.folders.CreateFolderAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], folder_path: str)

Stateless action that creates a folder on the mail server.

Parameters:
  • config – IMAP authentication configuration.

  • folder_path – Path of the folder to create.

Example:

>>> import aiviro
>>> from aiviro.actions.email import CreateFolderAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> CreateFolderAction(config=config, folder_path="Archive/2024")(robot=r)
class aiviro.actions.email.folders.DeleteFolderAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], folder_path: str)

Stateless action that deletes a folder from the mail server.

Parameters:
  • config – IMAP authentication configuration.

  • folder_path – Path of the folder to delete.

Example:

>>> import aiviro
>>> from aiviro.actions.email import DeleteFolderAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> DeleteFolderAction(config=config, folder_path="OldFolder")(robot=r)
class aiviro.actions.email.folders.FolderExistsAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')], folder_path: str)

Stateless action that checks if a folder exists on the mail server.

Parameters:
  • config – IMAP authentication configuration.

  • folder_path – Path of the folder to check.

Example:

>>> import aiviro
>>> from aiviro.actions.email import FolderExistsAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> exists = FolderExistsAction(config=config, folder_path="Archive")(robot=r)
>>> print(exists)
True
class aiviro.actions.email.folders.GetFolderDelimiterAction(config: Annotated[IMAPBasicConfig | IMAPOutlookOAuth2PublicConfig | IMAPOutlookOAuth2ConfidentialConfig | IMAPExchangelibConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')])

Stateless action that returns the folder delimiter used by the mail server. This is typically needed when working with basic IMAP authentication, as the delimiter is not standardized.

Parameters:

config – IMAP authentication configuration.

Example:

>>> import aiviro
>>> from aiviro.actions.email import GetFolderDelimiterAction, IMAPBasicConfig
>>>
>>> r = aiviro.create_desktop_robot()
>>> config = IMAPBasicConfig(server="imap.example.com", username="user", password="pass")
>>> delimiter = GetFolderDelimiterAction(config=config)(robot=r)
>>> print(delimiter)
/

Configuration

Each config model captures the parameters needed to instantiate the corresponding authorization connector from aiviro.modules.email.client.authorization.

The union types IMAPConfig and SMTPConfig are used in action signatures so that any supported auth method can be passed.

pydantic model aiviro.actions.email.configs.IMAPBasicConfig

Configuration for basic IMAP authentication (username + password).

field kind: Literal['basic'] = 'basic'
field oauth2: bool = False
field plain_login: bool = False
field port: int | None = None
field starttls: bool = False
field unencrypted: bool = False
property verified_port: int
pydantic model aiviro.actions.email.configs.SMTPBasicConfig

Configuration for basic SMTP authentication (username + password).

field email_address: str | None = None
field kind: Literal['basic'] = 'basic'
field oauth2: bool = False
field port: int | None = None
field starttls: bool = True
field unencrypted: bool = False
property verified_port: int
pydantic model aiviro.actions.email.configs.IMAPOutlookOAuth2PublicConfig

Configuration for Outlook OAuth2 public IMAP (interactive login).

field authority_url: str [Required]
field client_id: str [Required]
field kind: Literal['outlook_public'] = 'outlook_public'
field shared_mailbox: str | None = None
field username: str [Required]
property scopes: list[str]
pydantic model aiviro.actions.email.configs.SMTPOutlookOAuth2PublicConfig

Configuration for Outlook OAuth2 public SMTP (interactive login).

field authority_url: str [Required]
field client_id: str [Required]
field email_address: str | None = None
field kind: Literal['outlook_public'] = 'outlook_public'
field sender_name: str | None = None
field username: str [Required]
property scopes: list[str]
pydantic model aiviro.actions.email.configs.IMAPOutlookOAuth2ConfidentialConfig

Configuration for Outlook OAuth2 confidential IMAP (app secret).

field authority_url: str [Required]
field client_id: str [Required]
field kind: Literal['outlook_confidential'] = 'outlook_confidential'
field secret: str [Required]
field username: str [Required]
property scopes: list[str]
pydantic model aiviro.actions.email.configs.SMTPOutlookOAuth2ConfidentialConfig

Configuration for Outlook OAuth2 confidential SMTP (app secret).

field authority_url: str [Required]
field client_id: str [Required]
field email_address: str | None = None
field kind: Literal['outlook_confidential'] = 'outlook_confidential'
field secret: str [Required]
field sender_name: str | None = None
field username: str [Required]
property scopes: list[str]
pydantic model aiviro.actions.email.configs.IMAPExchangelibConfig

Configuration for Exchangelib-based IMAP access.

field email_address: str [Required]
field kind: Literal['exchangelib'] = 'exchangelib'
field oauth2: Annotated[ExchangeOAuth2Config | ExchangeOAuth2LegacyConfig | ExchangeOAuth2AuthorizationCodeConfig | ExchangeO365InteractiveConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')] | None = None
field service_endpoint: bool = False
pydantic model aiviro.actions.email.configs.SMTPExchangelibConfig

Configuration for Exchangelib-based SMTP sending.

field email_address: str [Required]
field kind: Literal['exchangelib'] = 'exchangelib'
field oauth2: Annotated[ExchangeOAuth2Config | ExchangeOAuth2LegacyConfig | ExchangeOAuth2AuthorizationCodeConfig | ExchangeO365InteractiveConfig, FieldInfo(annotation=NoneType, required=True, discriminator='kind')] | None = None
field service_endpoint: bool = False

Data Schemas

These models replace the legacy dataclasses from aiviro.modules.email.client and are the public-facing return types used by all email actions.

pydantic model aiviro.actions.email.schemas.Address

Represents an e-mail address with optional display name.

field address: str [Required]
field full: str [Required]
field name: str | None = None
as_tuple() str | tuple[str, str]

Return address as a string or (name, address) tuple.

classmethod from_email_address(data: imap_tools.EmailAddress | None) Address

Create from imap_tools.EmailAddress.

classmethod from_exchangelib(data: exchangelib.Mailbox | None) Address

Create from exchangelib.Mailbox.

classmethod from_outlook_dict(data: dict[str, str] | None) Address

Create from Microsoft Graph API address dictionary.

pydantic model aiviro.actions.email.schemas.EmailAttachment

Represents a single e-mail attachment.

The binary payload (payload) and raw size (size) are excluded from serialization via Field(exclude=True).

field content_disposition: str [Required]
field content_id: str [Required]
field content_type: str [Required]
field filename: str [Required]
field is_inline: bool = False
field payload: bytes = b''
field size: int = 0
as_email_message() EmailMessage

Convert attachment into an EmailMessage.

Only works for message/rfc822 content type.

Raises:

ValueError – When the attachment is not a message/rfc822 type.

classmethod from_email_bytes(data: bytes, filename: str, content_id: str = '') EmailAttachment

Create from raw e-mail bytes (message/rfc822).

classmethod from_exchangelib(data: exchangelib.FileAttachment) EmailAttachment

Create from exchangelib.FileAttachment.

classmethod from_mail(data: imap_tools.MailAttachment, is_inline: bool) EmailAttachment

Create from imap_tools.MailAttachment.

classmethod from_outlook(data: dict[str, Any]) EmailAttachment

Create from Microsoft Graph API attachment dictionary.

property content: bytes

Return the raw attachment payload.

property content_maintype: str

Primary MIME type (e.g. application).

property content_subtype: str

MIME subtype (e.g. pdf).

pydantic model aiviro.actions.email.schemas.EmailMessage

Represents a single e-mail message.

This is a pure data model — it holds no connection/connector reference. Operations like move, copy, delete, etc. are handled by separate action classes.

field bcc: list[Address] [Optional]
field body: str = ''
field cc: list[Address] [Optional]
field datetime: datetime | None = None
field file_attachments: list[EmailAttachment] [Optional]
field html: str = ''
field id: str = ''
field path: str = ''
field recipients: list[Address] [Optional]
field seen: bool = False
field sender: Address = Address(address='', full='', name=None)
field subject: str = ''
as_file_attachment(attachment_name: str) EmailAttachment

Convert message into an EmailAttachment (message/rfc822).

This allows an e-mail to be saved or sent as an attachment to another email.

Parameters:

attachment_name – Name of the attachment.

Example:

>>> from aiviro.actions.email import EmailMessage, FetchEmailsAction, SendEmailAction
>>>
>>> # Fetch emails
>>> emails = FetchEmailsAction(config=imap_config, limit=1)()
>>> email = emails[0]
>>>
>>> # Convert email to attachment, using subject as attachment name
>>> attachment = email.as_file_attachment(
...     attachment_name=email.subject.replace(" ", "_")
... )
>>>
>>> # Send email with attachment
>>> SendEmailAction(
...     config=smtp_config,
...     recipients="recipient@example.com",
...     subject="Email as attachment",
...     message="See email in the attachment",
...     attachments=[attachment],
... )()
classmethod from_exchangelib(mail: exchangelib.Message, path: str) EmailMessage

Create from exchangelib.Message.

Parameters:
  • mail – An exchangelib.Message instance.

  • path – Folder path where the message resides.

classmethod from_mail(mail: imap_tools.MailMessage, path: str) EmailMessage

Create from imap_tools.MailMessage.

Parameters:
  • mail – An imap_tools.MailMessage instance.

  • path – Folder path where the message resides.

classmethod from_outlook(imap_connector: _IMAPOutlookOAuth2, mail: dict[str, Any], path: str) EmailMessage

Create from Microsoft Graph API message dictionary.

Parameters:
  • imap_connector – An Outlook IMAP connector.

  • mail – Raw message dictionary from the Graph API.

  • path – Folder path where the message resides.

property recipient: Address

Return the first recipient.

property uuid: str

Return the unique identifier of the message.

Raises:

RuntimeError – When the id is empty or None.

pydantic model aiviro.actions.email.schemas.EmailFolder

Represents an e-mail folder / mailbox.

field delim: str [Required]
field flags: tuple[str, ...] [Optional]
field id: str [Required]
field name: str [Required]
classmethod from_exchangelib(folder: exchangelib.Folder) EmailFolder

Create from exchangelib.Folder.

classmethod from_folder_info(folder: imap_tools.FolderInfo) EmailFolder

Create from imap_tools.FolderInfo.

classmethod from_outlook(folder: OutlookFolder) EmailFolder

Create from Outlook folder object.