diff --git a/lib/apify/__tests__/sendApifyWebhookEmail.test.ts b/lib/apify/__tests__/sendApifyWebhookEmail.test.ts
new file mode 100644
index 00000000..a2044f63
--- /dev/null
+++ b/lib/apify/__tests__/sendApifyWebhookEmail.test.ts
@@ -0,0 +1,54 @@
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import { sendApifyWebhookEmail } from "@/lib/apify/sendApifyWebhookEmail";
+import { RECOUP_FROM_EMAIL } from "@/lib/const";
+
+vi.mock("@/lib/ai/generateText", () => ({
+ default: vi.fn(async () => ({ text: "
body
" })),
+}));
+const sendEmailWithResend = vi.fn();
+vi.mock("@/lib/emails/sendEmail", () => ({
+ sendEmailWithResend: (...a: unknown[]) => sendEmailWithResend(...a),
+}));
+
+const PROFILE = {
+ fullName: "Ashnikko",
+ username: "ashnikko",
+ url: "https://instagram.com/ashnikko",
+ profilePicUrl: "https://example.com/pic.jpg",
+ biography: "bio",
+ followersCount: 100,
+ followsCount: 10,
+ latestPosts: [],
+} as never;
+
+beforeEach(() => {
+ vi.clearAllMocks();
+ sendEmailWithResend.mockResolvedValue({ id: "email-1" });
+});
+
+describe("sendApifyWebhookEmail", () => {
+ it("BCCs recipients so no account can see another's address (chat#1855)", async () => {
+ const recipients = [
+ "manager@customer-a.com",
+ "label@customer-b.com",
+ "internal@recoupable.com",
+ ];
+ await sendApifyWebhookEmail(PROFILE, recipients);
+ const payload = sendEmailWithResend.mock.calls[0][0] as Record;
+ expect(payload.bcc).toEqual(recipients);
+ expect(payload.to).toEqual([RECOUP_FROM_EMAIL]);
+ });
+
+ it("never carries more than our own address in to/cc, regardless of recipient count", async () => {
+ const recipients = Array.from({ length: 25 }, (_, i) => `user${i}@example.com`);
+ await sendApifyWebhookEmail(PROFILE, recipients);
+ const payload = sendEmailWithResend.mock.calls[0][0] as Record;
+ const visible = [payload.to, payload.cc].flat().filter(Boolean);
+ expect(visible).toEqual([RECOUP_FROM_EMAIL]);
+ });
+
+ it("returns null and sends nothing when there are no recipients", async () => {
+ expect(await sendApifyWebhookEmail(PROFILE, [])).toBeNull();
+ expect(sendEmailWithResend).not.toHaveBeenCalled();
+ });
+});
diff --git a/lib/apify/sendApifyWebhookEmail.ts b/lib/apify/sendApifyWebhookEmail.ts
index 051db149..3510dccd 100644
--- a/lib/apify/sendApifyWebhookEmail.ts
+++ b/lib/apify/sendApifyWebhookEmail.ts
@@ -45,9 +45,13 @@ Latest Posts: ${(Array.isArray(profile.latestPosts) ? profile.latestPosts : []).
prompt,
});
+ // Recipients span multiple customer accounts (the social's watchers are
+ // resolved cross-tenant), so they must NEVER share a visible To: line —
+ // BCC only, with ourselves as the required To: (chat#1855).
return await sendEmailWithResend({
from: RECOUP_FROM_EMAIL,
- to: emails,
+ to: [RECOUP_FROM_EMAIL],
+ bcc: emails,
subject: `${profile.fullName ?? profile.username ?? "Your artist"} has new posts on Instagram`,
html: text,
});