X Enterprises
fastify-xemail

sendPersonalizedBulk

Send different email content to each recipient using Promise.allSettled — partial failures are reported, not thrown.

sendPersonalizedBulk

Send different email content to each recipient. Uses Promise.allSettled internally — individual delivery failures are captured in the result array rather than thrown, so a failure for one recipient does not abort the rest.

Signature

fastify.xEmail.sendPersonalizedBulk(
  messages: PersonalizedMessage[]
): Promise<PersonalizedResult[]>

interface PersonalizedMessage {
  to: string;
  subject: string;
  html: string;
  text?: string;
}

interface PersonalizedResult {
  success: boolean;
  to: string;
  statusCode?: number;  // present on success
  error?: string;       // present on failure
}

Params

NameTypeRequiredDescription
messagesPersonalizedMessage[]YesNon-empty array of per-recipient message objects.

Returns

Promise<PersonalizedResult[]> — One result object per input message in the same order.

PropertyTypeDescription
successbooleantrue if the individual send succeeded.
tostringThe recipient email address.
statusCodenumberHTTP status from SendGrid (on success).
errorstringError message (on failure).

Throws

  • [xEmail] 'messages' must be a non-empty array for sendPersonalizedBulk(). — empty or non-array input.

Individual send failures are not thrown — they appear as { success: false, to, error } entries in the result array.

Examples

Basic — send personalized welcome emails

fastify.post("/users/bulk-welcome", async (request) => {
  const users = await db.users.findByIds(request.body.userIds);

  const messages = users.map((user) => ({
    to: user.email,
    subject: `Welcome, ${user.firstName}!`,
    html: `<p>Hi ${user.firstName}, your account is ready. <a href="${user.activationUrl}">Activate now</a>.</p>`,
  }));

  const results = await fastify.xEmail.sendPersonalizedBulk(messages);

  const failed = results.filter((r) => !r.success);
  if (failed.length > 0) {
    fastify.log.warn({ failed }, "Some bulk emails failed to send");
  }

  return { sent: results.filter((r) => r.success).length, failed: failed.length };
});

Realistic — subscription renewal reminders with error tracking

fastify.post("/subscriptions/send-renewal-reminders", async (request) => {
  const expiring = await db.subscriptions.getExpiringInDays(7);

  const messages = expiring.map((sub) => ({
    to: sub.userEmail,
    subject: "Your subscription expires soon",
    html: `
      <p>Hi ${sub.userName},</p>
      <p>Your ${sub.planName} subscription expires on ${sub.expiresAt}.</p>
      <p><a href="https://app.example.com/billing/renew?sub=${sub.id}">Renew now</a></p>
    `,
  }));

  const results = await fastify.xEmail.sendPersonalizedBulk(messages);

  const failedEmails = results
    .filter((r) => !r.success)
    .map((r) => ({ email: r.to, reason: r.error }));

  await db.emailLog.record({ type: "renewal_reminder", results, failedEmails });

  return {
    total: results.length,
    sent: results.filter((r) => r.success).length,
    failed: failedEmails,
  };
});

See Also

  • send-bulk — Same content to all recipients in a single API call
  • send-template — Dynamic template email for a single recipient

AI Context

package: "@xenterprises/fastify-xemail"
method: fastify.xEmail.sendPersonalizedBulk(recipients, options?)
use-when: Send different content to each recipient concurrently using Promise.allSettled — partial failures don't abort the batch
params: recipients (array of {to, subject, html, ...}), options
returns: Promise<Array<{ status, recipient, error? }>>
Copyright © 2026