diff --git a/src/batch/batch.spec.ts b/src/batch/batch.spec.ts
new file mode 100644
index 00000000..acd95706
--- /dev/null
+++ b/src/batch/batch.spec.ts
@@ -0,0 +1,96 @@
+import { enableFetchMocks } from 'jest-fetch-mock';
+import { Resend } from '../resend';
+
+enableFetchMocks();
+
+const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
+
+describe('Batch', () => {
+ afterEach(() => fetchMock.resetMocks());
+
+ describe('create', () => {
+ it('sends multiple emails', async () => {
+ const payload = [
+ {
+ from: 'bu@resend.com',
+ to: 'zeno@resend.com',
+ subject: 'Hello World',
+ html: '
Hello world
',
+ },
+ {
+ from: 'vitor@resend.com',
+ to: 'zeno@resend.com',
+ subject: 'Olá mundo',
+ html: 'olá mundo
',
+ },
+ {
+ from: 'bu@resend.com',
+ to: 'vitor@resend.com',
+ subject: 'Hi there',
+ html: 'Hi there
',
+ },
+ ];
+
+ fetchMock.mockOnce(
+ JSON.stringify({
+ data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }],
+ }),
+ {
+ status: 200,
+ headers: {
+ 'content-type': 'application/json',
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
+ },
+ },
+ );
+
+ const data = await resend.batch.create(payload);
+ expect(data).toMatchObject({
+ data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }],
+ });
+ });
+ });
+
+ describe('send', () => {
+ it('sends multiple emails', async () => {
+ const payload = [
+ {
+ from: 'bu@resend.com',
+ to: 'zeno@resend.com',
+ subject: 'Hello World',
+ html: 'Hello world
',
+ },
+ {
+ from: 'vitor@resend.com',
+ to: 'zeno@resend.com',
+ subject: 'Olá mundo',
+ html: 'olá mundo
',
+ },
+ {
+ from: 'bu@resend.com',
+ to: 'vitor@resend.com',
+ subject: 'Hi there',
+ html: 'Hi there
',
+ },
+ ];
+
+ fetchMock.mockOnce(
+ JSON.stringify({
+ data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }],
+ }),
+ {
+ status: 200,
+ headers: {
+ 'content-type': 'application/json',
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
+ },
+ },
+ );
+
+ const data = await resend.batch.send(payload);
+ expect(data).toMatchObject({
+ data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }],
+ });
+ });
+ });
+});
diff --git a/src/batch/batch.ts b/src/batch/batch.ts
new file mode 100644
index 00000000..bc895856
--- /dev/null
+++ b/src/batch/batch.ts
@@ -0,0 +1,39 @@
+import { render } from '@react-email/render';
+import * as React from 'react';
+import { Resend } from '../resend';
+import {
+ CreateBatchOptions,
+ CreateBatchRequestOptions,
+ CreateBatchResponse,
+} from './interfaces';
+
+export class Batch {
+ constructor(private readonly resend: Resend) {}
+
+ async send(
+ payload: CreateBatchOptions,
+ options: CreateBatchRequestOptions = {},
+ ): Promise {
+ return this.create(payload, options);
+ }
+
+ async create(
+ payload: CreateBatchOptions,
+ options: CreateBatchRequestOptions = {},
+ ): Promise {
+ for (const email of payload) {
+ if (email.react) {
+ email.html = render(email.react as React.ReactElement);
+ delete email.react;
+ }
+ }
+
+ const data = await this.resend.post(
+ '/emails/batch',
+ payload,
+ options,
+ );
+
+ return data;
+ }
+}
diff --git a/src/batch/interfaces/create-batch-options.interface.ts b/src/batch/interfaces/create-batch-options.interface.ts
new file mode 100644
index 00000000..17e591e0
--- /dev/null
+++ b/src/batch/interfaces/create-batch-options.interface.ts
@@ -0,0 +1,13 @@
+import { PostOptions } from '../../common/interfaces';
+import { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface';
+
+export type CreateBatchOptions = CreateEmailOptions[];
+
+export interface CreateBatchRequestOptions extends PostOptions {}
+
+export interface CreateBatchResponse {
+ data: {
+ /** The ID of the newly created email. */
+ id: string;
+ }[];
+}
diff --git a/src/batch/interfaces/index.ts b/src/batch/interfaces/index.ts
new file mode 100644
index 00000000..306bcd76
--- /dev/null
+++ b/src/batch/interfaces/index.ts
@@ -0,0 +1 @@
+export * from './create-batch-options.interface';
diff --git a/src/resend.ts b/src/resend.ts
index 9b9c1aa5..7310262d 100644
--- a/src/resend.ts
+++ b/src/resend.ts
@@ -1,8 +1,7 @@
-import * as React from 'react';
-import { render } from '@react-email/render';
import { version } from '../package.json';
-import { GetOptions, PostOptions, PutOptions } from './common/interfaces';
import { ApiKeys } from './api-keys/api-keys';
+import { Batch } from './batch/batch';
+import { GetOptions, PostOptions, PutOptions } from './common/interfaces';
import { Domains } from './domains/domains';
import { Emails } from './emails/emails';
import { CreateEmailOptions, CreateEmailResponse } from './emails/interfaces';
@@ -18,6 +17,7 @@ export class Resend {
readonly apiKeys = new ApiKeys(this);
readonly domains = new Domains(this);
readonly emails = new Emails(this);
+ readonly batch = new Batch(this);
constructor(readonly key?: string) {
if (!key) {
@@ -100,27 +100,7 @@ export class Resend {
return await this.fetchRequest(path, requestOptions);
}
- async sendEmail(data: CreateEmailOptions) {
- const path = '/email';
-
- if (data.react) {
- data.html = render(data.react as React.ReactElement);
- delete data.react;
- }
-
- const response = await this.post(path, {
- from: data.from,
- to: data.to,
- bcc: data.bcc,
- cc: data.cc,
- reply_to: data.reply_to,
- subject: data.subject,
- text: data.text,
- html: data.html,
- attachments: data.attachments,
- tags: data.tags,
- });
-
- return response;
+ async sendEmail(data: CreateEmailOptions): Promise {
+ return this.emails.create(data);
}
}