Skip to content

Send cc and bcc through the six providers that were dropping them - #62

Merged
eelcoj merged 1 commit into
Rails-Designer:mainfrom
pcbeingused333:cc-bcc-missing-providers
Sep 7, 2026
Merged

eelcoj merged 1 commit into
Rails-Designer:mainfrom
pcbeingused333:cc-bcc-missing-providers

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

Closes #58.

Options accepts cc: and bcc: on every email, but six providers never read them, so the addresses went out discarded with no warning. This adds them, in the shape each API takes, through the address_list helper from #61:

provider shape
Mailgun cc / bcc, comma-separated line
MailPace cc / bcc, comma-separated line
Postmark Cc / Bcc, comma-separated line
Mailjet Cc / Bcc, array of {"Email" => …}
SendGrid cc / bcc inside the personalization, array of {"email" => …}
SparkPost see below

Empty and whitespace-only values are dropped rather than sent, same as the providers that already used the helper.

SparkPost

SparkPost has no cc/bcc field. Every copy is an entry in recipients, and a copy is only shown as one if its address is repeated in the CC header — so the difference between a cc and a bcc is the header, not the recipient list. header_to carries the To line for all of them, which is also what keeps a bcc hidden: without it SparkPost sets each recipient's visible To to their own address, so the bcc would see itself addressed directly.

{"content" => {..., "headers" => {"CC" => "copy@example.com"}},
 "recipients" => [
   {"address" => {"email" => "first@example.com",  "header_to" => "first@example.com, second@example.com"}},
   {"address" => {"email" => "second@example.com", "header_to" => "first@example.com, second@example.com"}},
   {"address" => {"email" => "copy@example.com",   "header_to" => "first@example.com, second@example.com"}},
   {"address" => {"email" => "archive@example.com","header_to" => "first@example.com, second@example.com"}}]}

No cc means no headers key at all, so a send without copies keeps the body it had.

A second bug this fixes

Reading the recipients through the same helper also fixes #59. That issue is closed, but it still reproduces on main at 4d25da7 — and not only in Mailjet: SendGrid and SparkPost put a multi-address to into a single address object too.

options = Courrier::Email::Options.new(from: "d@example.com", to: "a@example.com, b@example.com", subject: "s", text: "t")

Courrier::Email::Providers::Mailjet.new(api_key: "k", options: options, provider_options: config).body
# => "To" => [{"Email" => "a@example.com, b@example.com"}]
Courrier::Email::Providers::Sendgrid.new(api_key: "k", options: options).body["personalizations"]
# => [{"to" => [{"email" => "a@example.com, b@example.com"}]}]
Courrier::Email::Providers::Sparkpost.new(api_key: "k", options: options).body["recipients"]
# => [{"address" => {"email" => "a@example.com, b@example.com"}}]

Happy to pull that out into its own PR if you'd rather review it separately, but it is the same one-line change per provider as the cc/bcc work, so splitting it would leave To broken next to a working Cc.

Left alone

Loops and Userlist address a single contact through a template API and have nowhere to put a copy, so they still ignore cc/bcc. Worth a note in the README, or a raise, but that is a decision rather than a fix — happy to follow whichever you prefer.

Tests

Postmark, SendGrid, SparkPost and MailPace had no test files; they have one now, covering the body shape, the empty-value handling and the auth header. Mailgun's and Mailjet's were extended.

bundle exec rake test on Ruby 3.4: 159 runs, 302 assertions, 0 failures, 0 errors. standardrb clean. With lib/ reverted and the new tests kept, 8 of them fail — verified rather than assumed.

Options accepts cc: and bcc: on every email, but Mailgun, Mailjet, MailPace,
Postmark, SendGrid and SparkPost never read them, so the addresses were
discarded without a warning (Rails-Designer#58).

Each provider now maps them through address_list, in the shape its own API
takes: a comma-separated line for Mailgun, MailPace and Postmark; an array of
address objects for Mailjet and SendGrid. SparkPost has no cc/bcc field, so the
copies join recipients and only the cc addresses are repeated in the CC header,
with header_to holding the To line so a bcc stays hidden.

Reading the recipient lists through the same helper also fixes Mailjet, SendGrid
and SparkPost sending several to: addresses as one malformed address (Rails-Designer#59).

Loops and Userlist are left alone: their APIs address a single contact and have
nowhere to put a copy.
@eelcoj
eelcoj merged commit 14c5c5f into Rails-Designer:main Sep 7, 2026
1 check passed
@eelcoj

eelcoj commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Thanks again for this @pcbeingused333! ❤️ Will push a new version shorttly.

@pcbeingused333
pcbeingused333 deleted the cc-bcc-missing-providers branch September 7, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mailjet sends multiple recipients as a single malformed address cc and bcc are silently dropped by 8 of 14 providers

2 participants