Outbound Email
MailAtlas can compose, render, store, and send outbound email through providers you configure at runtime. Use this workflow when your application needs a local audit record for messages it sends.
Outbound email uses the same local-first design as inbound ingest: MailAtlas writes inspectable artifacts first, then contacts the provider unless the message is a dry run.
MailAtlas is not a hosted deliverability service. It does not manage reputation, suppression lists, campaign analytics, bounce processing, or provider accounts.
Every outbound message gets a local record before provider delivery. That record can include:
- A rendered raw
.emlsnapshot. - Plain-text body files.
- HTML body files.
- Copied attachments.
- Recipient metadata.
- BCC metadata in SQLite.
- Provider name.
- Status.
- Error details.
- Retry metadata.
- Provider response metadata.
Provider secrets and OAuth tokens are read at runtime. MailAtlas does not write SMTP passwords, Cloudflare API tokens, Gmail access tokens, or Gmail refresh tokens into store.db, raw snapshots, logs, or JSON send results.
Start with a dry run
Section titled “Start with a dry run”Use --dry-run to verify rendering, validation, attachments, and local storage without contacting a provider:
mailatlas send \ --dry-run \ --subject "Build complete" \ --text "The build passed."The command prints JSON with the outbound record ID and stores rendered files under outbound/ in the workspace root.
Use dry runs when testing message rendering, generated content, attachments, headers, review workflows, or provider setup.
Provider choice
Section titled “Provider choice”Choose a provider with --provider or MAILATLAS_SEND_PROVIDER.
| Provider | Use it when | Credentials |
|---|---|---|
smtp | You already have an SMTP relay or local mail test server. | SMTP host, optional username and password. |
cloudflare | You use Cloudflare Email Service for API-based sending. | Cloudflare account ID and API token. |
gmail | You want to send from a personal Gmail address or Gmail send-as alias. | Gmail API OAuth token with the gmail.send scope. |
export MAILATLAS_SEND_PROVIDER=smtpexport MAILATLAS_SMTP_HOST=smtp.example.comexport MAILATLAS_SMTP_USERNAME=agent@example.comexport MAILATLAS_SMTP_PASSWORD=app-password
mailatlas send \ --subject "SMTP test" \ --text "Sent through SMTP."Useful SMTP variables:
MAILATLAS_SMTP_HOSTMAILATLAS_SMTP_PORTMAILATLAS_SMTP_USERNAMEMAILATLAS_SMTP_PASSWORDMAILATLAS_SMTP_STARTTLSMAILATLAS_SMTP_SSL
For Gmail addresses, SMTP app passwords are a compatibility path. Prefer the Gmail provider when OAuth is available.
Cloudflare Email Service
Section titled “Cloudflare Email Service”export MAILATLAS_SEND_PROVIDER=cloudflareexport MAILATLAS_CLOUDFLARE_ACCOUNT_ID=your-account-idexport MAILATLAS_CLOUDFLARE_API_TOKEN=your-api-token
mailatlas send \ --subject "Cloudflare test" \ --text "Sent through Cloudflare Email Service."Useful Cloudflare variables:
MAILATLAS_CLOUDFLARE_ACCOUNT_IDMAILATLAS_CLOUDFLARE_API_TOKENMAILATLAS_CLOUDFLARE_API_BASE
Use the Cloudflare API base override only for tests or provider-compatible gateways.
Gmail API OAuth
Section titled “Gmail API OAuth”Use the Gmail provider for personal Gmail addresses and Gmail-configured send-as aliases:
python -m pip install "mailatlas[keychain]"
mailatlas auth gmail \ --client-id "$MAILATLAS_GMAIL_CLIENT_ID" \ --client-secret "$MAILATLAS_GMAIL_CLIENT_SECRET" \
mailatlas send \ --provider gmail \ --subject "Gmail API test" \ --text "Sent with Gmail API OAuth."MailAtlas requests only the https://www.googleapis.com/auth/gmail.send scope by default.
For backend applications, store Gmail refresh tokens in your own encrypted credential store and pass short-lived access tokens to MailAtlas at send time.
Attachments and headers
Section titled “Attachments and headers”mailatlas send \ --subject "Report" \ --text-file report-summary.txt \ --attach report.pdf \ --header "X-Campaign-ID: weekly"MailAtlas validates sender and recipient fields, rejects CR/LF header injection, and fails if an attachment path is missing.
BCC behavior
Section titled “BCC behavior”BCC recipients are stored in SQLite for audit and are included in provider delivery. They are omitted from local raw MIME snapshots.
Provider behavior:
- SMTP sends BCC through the SMTP envelope.
- Cloudflare sends BCC through the provider payload.
- Gmail API sends use a provider-only transient MIME payload that includes BCC for delivery while keeping the saved local raw snapshot Bcc-free.
Default list views should not include BCC recipients. Use explicit detail views or audit options when BCC visibility is required.
Idempotency
Section titled “Idempotency”Use an idempotency key when retrying a send command from scripts:
mailatlas send \ --provider gmail \ --subject "Retry-safe test" \ --text "This command can be retried." \ --idempotency-key gmail-api-test-1If the same key already exists, MailAtlas returns the existing outbound record instead of sending a second message.
Status lifecycle
Section titled “Status lifecycle”| Status | Meaning |
|---|---|
draft | Message was rendered and stored as a local draft. |
dry_run | Message was rendered and stored without contacting a provider. |
sending | Message is in the process of being sent. |
sent | Provider accepted the send request. |
queued | Provider accepted or queued the message but final delivery is not known. |
error | Provider send or validation failed. |
Python API
Section titled “Python API”from mailatlas import MailAtlas, OutboundMessage, SendConfig
atlas = MailAtlas()
result = atlas.send_email( OutboundMessage( subject="Build complete", text="The build passed.", idempotency_key="build-123", ), SendConfig(provider="smtp", dry_run=True),)Troubleshooting
Section titled “Troubleshooting”Dry run succeeds but send fails
Section titled “Dry run succeeds but send fails”Check provider configuration and credentials. A dry run validates local rendering but does not verify provider authentication.
Attachment fails
Section titled “Attachment fails”Confirm the attachment path exists and is readable from the current working directory.
Gmail From address fails
Section titled “Gmail From address fails”Use the authenticated Gmail address or a Gmail send-as alias configured in Gmail.
Duplicate send avoided
Section titled “Duplicate send avoided”If you reuse an idempotency key, MailAtlas returns the existing outbound record instead of sending again. Use a new key only when you intentionally want a new send.