How the Email Suppression Handler Works and Manages Gmail Attachments in Macro

The email suppression handler is an AWS Lambda function that processes Amazon SES bounce and complaint notifications via SNS to maintain a real-time blocked-email list, while forwarded Gmail attachments are fetched on-demand directly from the Gmail API rather than stored in Macro's own infrastructure.

The macro-inc/macro repository implements a robust email deliverability system that safeguards outgoing message reputation by automatically suppressing problematic addresses. Understanding how this handler processes SES notifications and manages external attachment sources reveals the architecture behind reliable email delivery in the Macro platform.

How the Email Suppression Handler Processes SES Events

The suppression pipeline begins at the AWS Lambda entry point in services/email_suppression_handler/src/handler.rs. The handler function receives an SnsEvent containing one or more SES notification messages, dispatching each record to handle_email_notification for processing.

Entry Point and Deserialization

Inside handle_email_notification, the raw JSON payload from SNS is deserialized into the EmailNotification enum defined in services/email_suppression_handler/src/model.rs. This enum distinguishes between NotificationType::Bounce and NotificationType::Complaint events using serde_json, routing each type to its specific handler function.

// Simplified example of processing a bounce notification
let payload = r#"
{
  "notificationType": "Bounce",
  "bounce": {
    "bounceType": "Permanent",
    "bounced_recipients": [{"emailAddress": "bad@example.com"}]
  }
}
"#;

// Inside the Lambda handler
handle_email_notification(&db_pool, payload).await?;

Handling Permanent Bounces

The handle_bounce_notification function inspects the bounce_type field to determine suppression behavior. When the type is Permanent, indicating a hard bounce, every address in the bounced_recipients array is collected and passed to the database layer. Transient bounces (such as MailboxFull or AttachmentRejected) are logged but do not trigger blocks, and Undetermined bounces receive similar treatment to avoid suppressing valid addresses prematurely.

Processing Complaint Notifications

For complaints handled by handle_complaint_notification, the logic checks for a complaint_sub_type first. If present (for example, OnAccountSuppressionList), the system treats this as a hard suppression immediately. When no sub-type exists, the handler inspects complaint_feedback_type:

  • Triggers block: Abuse, AuthFailure, Fraud, Virus, Other
  • Ignored: NotSpam

This granular filtering ensures only legitimate abuse signals affect deliverability while preserving send capability for false-positive reports.

Database Operations for Blocked Emails

Once the handler identifies addresses requiring suppression, it calls macro_db_client::blocked_email::bulk_upsert_block_email from crates/macro_db_client/src/blocked_email.rs. This function performs idempotent upserts into the blocked_email table, ensuring that duplicate notifications do not create multiple records while enabling fast lookups by the email-sending services before dispatching future messages.

How Gmail Attachments Are Managed in Macro

Unlike standard attachments that might reside in S3, forwarded Gmail attachments follow a zero-storage-retrieval pattern. The system fetches raw attachment bytes directly from Google's servers at send-time, eliminating storage duplication and ensuring recipients receive the latest version of the file.

On-Demand Fetching from Gmail API

The GmailApi client defined in services/email_service/src/util/gmail/send.rs and services/email_service/src/util/upload_attachment.rs handles authentication with Google before calling users.messages.attachments.get. The fetch_attachment method constructs requests using the Gmail message ID and attachment ID, respects Gmail-specific rate-limit headers, and converts the Base-64 encoded response into raw bytes for inclusion in the outgoing email.

let gmail_api = GmailApi::for_account(&account).await?;
let attachment_data = gmail_api
    .fetch_attachment(message_id, attachment_id)
    .await
    .context("Failed to fetch attachment from Gmail")?;
email.send_with_attachment(attachment_data).await?;

Error Handling for Attachment Retrieval

If fetch_attachment encounters network failures, permission errors, or missing resources, it returns a GmailFetchFailed error that bubbles up to the caller. This fail-fast approach prevents partial message delivery, ensuring that emails missing critical attachments are never sent to recipients.

Summary

  • The email suppression handler is an AWS Lambda function in services/email_suppression_handler/src/handler.rs that processes Amazon SES notifications via SNS to maintain deliverability.
  • Permanent bounces and specific complaint types trigger immediate insertion into the blocked_email table via bulk_upsert_block_email, while transient bounces are logged only.
  • Gmail attachments are retrieved on-demand from the Gmail API at send-time rather than stored in Macro's S3 buckets, utilizing the GmailApi client in services/email_service/src/util/gmail/send.rs.
  • Error handling for attachment fetching is strict, with GmailFetchFailed errors preventing message delivery if attachments cannot be retrieved.

Frequently Asked Questions

What triggers an email address to be added to the blocked list?

According to the source code in services/email_suppression_handler/src/handler.rs, addresses are blocked when SES reports a Permanent bounce type or when complaint notifications contain specific sub-types like OnAccountSuppressionList or feedback types including Abuse, Fraud, or Virus. Transient bounces and NotSpam complaints do not trigger blocks.

Why doesn't Macro store Gmail attachments in S3?

The architecture avoids storing Gmail attachments in Macro's infrastructure to prevent data duplication and ensure recipients always receive the most current version of the file. By fetching directly from the Gmail API at send-time using the fetch_attachment method in services/email_service/src/util/gmail/send.rs, the system maintains lightweight storage while guaranteeing attachment freshness.

What happens when the Gmail API fails to return an attachment?

If the GmailApi client encounters errors during fetch_attachment, it returns a GmailFetchFailed error that propagates up the call stack. This causes the entire send operation to fail immediately, ensuring that Macro never delivers emails advertising attachments that weren't successfully retrieved from Gmail's servers.

How does the handler distinguish between bounce severity levels?

The handler examines the bounceType field within SES notifications. Permanent bounces indicate non-existent domains or addresses and trigger immediate blocking. Transient bounces (like full mailboxes) and Undetermined bounces are logged for observability but do not result in address suppression, preserving the ability to retry delivery to those recipients later.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →