fix(Resend): send attachments via per-message endpoint#120
Conversation
Resend's batch endpoint (/emails/batch) silently drops the attachments field, so any attachment-bearing send through the Resend adapter never delivered the attachment to the recipient. When the message has attachments, route each recipient as an individual request to /emails (which accepts attachments). The no-attachment path keeps using /emails/batch so throughput is unchanged. Also raises the per-send size limit to 40MB to match Resend's documented single-send limit, and adds routing tests that mock the HTTP layer to assert which endpoint is used and that the attachments payload is shaped correctly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Greptile SummaryThis PR fixes the Resend adapter silently dropping attachments by routing to the correct API endpoint depending on whether attachments are present. The attachment size cap is also raised from 25 MB to 40 MB to match Resend's documented single-send limit.
Confidence Score: 4/5Safe to merge; the core routing logic is correct and well-tested, with the no-attachment batch path left untouched. The attachment-routing fix is straightforward and the new tests validate all four key scenarios. The only open question is that getMaxMessagesPerRequest() still caps at 100, so a large recipient list with attachments triggers up to 100 sequential HTTP calls with no rate-limit handling — a practical concern for high-volume senders but not a correctness defect in the changed code. The sequential-request loop in sendIndividually inside Resend.php deserves a second look if this adapter will be used with large recipient lists and attachments. Important Files Changed
|
Symptom
Sending an email with attachments via the
Resendadapter currently results in the recipient receiving the email without the attachments. The attachments are silently dropped.Root cause
The adapter has always posted to Resend's batch endpoint
POST /emails/batchfor every send. That endpoint only acceptsfrom,to,subject,html,text,cc,bcc,reply_to,headers,scheduled_at,tags, etc. — it does not supportattachmentsorscheduled_atand silently ignores them. See Send batch emails — the docs explicitly call this out:Resend's single-send endpoint
POST /emailsdoes supportattachments. See Send email.Fix
Resend::process()now branches on whether the message carries attachments:/emails/batchwith all recipients in one request — throughput unchanged for the common case./emails, each with theattachmentsarray (filename/content/content_type) embedded. Per-recipient outcomes are aggregated into the sameResponseshape the batch path returns, so partial success is reported correctly.The per-email size limit is also raised to 40 MB to match Resend's documented single-send cap.
Test plan
composer lint(Pint, PSR-12) passescomposer analyse(PHPStan level 6) passesResendRoutingTest(4 tests, 25 assertions) passes — stubsrequest()and asserts:/emails/batchonce with all recipients in the body/emailsonce per recipient with theattachmentsarray shaped correctlydeliveredToand per-recipientstatus/errorResendTestcontinue to requireRESEND_API_KEYand a verified test address)