Skip to content

SendGrid: support inline CID logo attachment to avoid external content warnings #37846

Description

@madhuv30

Problem

Email clients (especially Microsoft Outlook) block external images in emails sent via SendGrid, prompting recipients with a "Do you want to download external content?" warning before rendering the logo.

This happens because SendGridEmailLogger.send_email() sends HTML with an external <img src="{email_logo_url}"> URL. When the email was sent via SMTP through a corporate mail relay, Outlook trusted the sender and loaded images automatically. With SendGrid, the email originates from an external sender, so Outlook treats all linked content as untrusted.

Proposed Solution

Support embedding the logo as an inline CID attachment in the SendGrid Mail Send API payload. When a logo file path is configured, the image should be base64-encoded and attached with disposition: inline and a content_id, then the {email_logo_url} reference in the HTML body should be replaced with cid:<content_id>.

Suggested Changes

1. litellm_enterprise/enterprise_callbacks/send_emails/sendgrid_email.py

In __init__:

  • Read a new env var EMAIL_LOGO_PATH (file path to the logo image)
  • If set, read the file, base64-encode it, and store as a pre-built attachment dict:
    {
        "content": "<base64-encoded-bytes>",
        "type": "image/png",
        "filename": "logo.png",
        "disposition": "inline",
        "content_id": "logo"
    }
  • If the file is missing or unset, fall back to the current external URL behavior (no breaking change)

In send_email():

  • If the logo attachment is available:
    1. Replace the EMAIL_LOGO_URL value in html_body with cid:logo
    2. Add "attachments": [<logo_attachment>] to the SendGrid JSON payload

2. No template changes needed

The existing {email_logo_url} placeholder in email templates (key_created_email.py, key_rotated_email.py, user_invitation_email.py) stays as-is. The URL-to-CID replacement happens at send time in send_email().

3. No changes to base_email.py

The EmailParams.logo_url resolution logic remains unchanged. The CID substitution is isolated to the SendGrid transport layer.

Why This Matters

  • Outlook desktop (the most common corporate email client) blocks external images by default for emails from external senders
  • CID inline attachments are rendered without warnings in all major email clients (Outlook, Gmail, Apple Mail, Thunderbird)
  • The logo file is only ~5 KB, so the email size increase is negligible
  • SMTP-based sending did not have this issue because corporate mail relays are trusted senders

Environment

  • litellm version: 1.94.3
  • Email provider: SendGrid (Mail Send API v3)
  • Affected email client: Microsoft Outlook (desktop and OWA)

Workaround

We are currently patching sendgrid_email.py at container startup to add this behavior. Would be great to have native support so we can drop the patch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions