# Send email with the Node.js SDK

Install the prontuno package, configure your keys, and send email, attachments, and batches from Node.js.

This guide shows how to send transactional email from a Node.js application with the official `prontuno` package. It also works with Bun, Deno 2, and Cloudflare Workers.

## Before you begin

- [Verify a sending domain](https://prontuno.com/docs/sending/verify-sending-domain/).
- [Create a Sending API key](https://prontuno.com/docs/sending/api-keys/) for that domain.
- Use Node.js 22 or newer.

## Install the SDK

```bash
npm install prontuno
```

With Bun, run `bun add prontuno`. With Deno, import from `npm:prontuno`. The package has no runtime dependencies and supports both `import` and `require`.

## Configure the client

Store the Sending API key in an environment variable, such as `PRONTUNO_SENDING_KEY`, and never in source control or browser code. Then create a client:

```typescript
import { Prontuno } from "prontuno";

const prontuno = new Prontuno({
    sendingKey: process.env.PRONTUNO_SENDING_KEY,
});
```

Create one client and reuse it across requests.

If the same application also reads email logs or manages webhooks, add a Management key as `managementKey`. The client uses the Sending key for sends and the Management key for everything else.

## Send an email

```typescript
const result = await prontuno.emails.send(
    {
        from: "Acme <receipts@example.com>",
        to: "ada@example.com",
        subject: "Your receipt",
        html: "<p>Thanks for your order.</p>",
        text: "Thanks for your order.",
    },
    { idempotencyKey: "order-1042-receipt" },
);

for (const email of result.emails) {
    console.log(email.id, email.status);
}
```

The `from` address must be on the domain the Sending key belongs to. Every email needs an `html` body, a `text` body, or both.

The response contains one entry for each recipient. Its `status` is `queued` when Prontuno accepted the email, or `rejected` with a `rejection_code` and `rejection_reason`, for example when the address is on your [suppression list](https://prontuno.com/docs/deliverability/suppressions/). Store the `id` to look the email up later.

The idempotency key makes the send safe to retry. If the request times out, the SDK retries it with the same key, and Prontuno sends the email only once. Use a key that identifies the event that triggered the email.

## Send to several recipients

`to`, `cc`, and `bcc` each accept one address or an array of addresses, up to 100 recipients in total:

```typescript
await prontuno.emails.send({
    from: "Acme <reports@example.com>",
    to: ["ada@example.com", "grace@example.com"],
    cc: "team@example.com",
    reply_to: "support@example.com",
    subject: "Weekly report",
    text: "This week's report is ready.",
});
```

## Attach files

Pass file contents as a `Buffer`, and the SDK encodes them for you:

```typescript
import { readFile } from "node:fs/promises";

await prontuno.emails.send({
    from: "Acme <billing@example.com>",
    to: "ada@example.com",
    subject: "Your invoice",
    text: "Your invoice is attached.",
    attachments: [
        {
            filename: "invoice.pdf",
            content: await readFile("invoice.pdf"),
            content_type: "application/pdf",
        },
    ],
});
```

String content is read as base64. For plain text, set `encoding: "utf8"`. To show an image inside the HTML body, give the attachment a `content_id` and reference it as `cid:` in an `img` tag. An email with its attachments must be 25 MB or less.

## Send a batch

Send up to 1,000 emails in one request:

```typescript
const batch = await prontuno.emails.sendBatch(
    [
        {
            from: "hello@example.com",
            to: "ada@example.com",
            subject: "Hi Ada",
            text: "Hi Ada",
        },
        {
            from: "hello@example.com",
            to: "grace@example.com",
            subject: "Hi Grace",
            text: "Hi Grace",
        },
    ],
    { idempotencyKey: "welcome-batch-2026-10-24" },
);
```

Prontuno validates every email before accepting any of them, so an invalid item rejects the whole batch and nothing is sent. `batch.emails` holds one array of results for each email, in the order you sent them.

## Send from another domain

Each Sending key belongs to one domain. To send from a second domain, create a Sending key for it, then get a client that uses it:

```typescript
const billing = prontuno.withSendingKey(process.env.PRONTUNO_BILLING_SENDING_KEY);

await billing.emails.send({
    from: "Acme Billing <invoices@billing.example.com>",
    to: "ada@example.com",
    subject: "Your invoice",
    text: "Your invoice is attached.",
});
```

The new client keeps every other setting, including the Management key.

## Handle errors

When the API rejects a request, the SDK throws a `ProntunoApiError`:

```typescript
import { ProntunoApiError } from "prontuno";

try {
    await prontuno.emails.send(email, { idempotencyKey });
} catch (error) {
    if (error instanceof ProntunoApiError) {
        console.error(error.status, error.code, error.message);
        console.error(error.requestId); // include this when contacting support

        if (error.retryable) {
            // Safe to try again later with the same idempotency key.
        }
    }

    throw error;
}
```

`error.details` lists the fields that failed validation, and for a batch, which item they belong to. `error.suggestedAction` names the next step, such as `fix_input` or `top_up_credits`. Network failures throw `ProntunoConnectionError`, and timeouts throw `ProntunoTimeoutError`.

## Check delivery

A `queued` status means Prontuno accepted the email, not that it was delivered. To follow what happens next:

- Look the email up by its ID with `prontuno.emails.retrieve(id)`, which needs a Management key.
- Receive delivery, bounce, and complaint events as they happen with [webhooks](https://prontuno.com/docs/integrations/verify-webhooks-with-an-sdk/).
- Open **Sent emails** in the dashboard. See [Understand email statuses and logs](https://prontuno.com/docs/deliverability/understand-email-statuses-and-logs/).

To test bounces and deferrals without emailing real people, send to the addresses in [Mailbox Simulator](https://prontuno.com/docs/deliverability/test-with-mailbox-simulator/).

## Use Bun, Deno, or Cloudflare Workers

The SDK has no runtime dependencies. It runs anywhere that provides `fetch`, `node:crypto`, and `node:buffer`.

- **Bun:** install with `bun add prontuno`. Nothing else changes.
- **Deno 2 and Deno Deploy:** import from `npm:prontuno`.
- **Cloudflare Workers:** turn on Node.js compatibility. It is on by default when the Worker’s `compatibility_date` is 2026-08-04 or later. With an earlier date, add the `nodejs_compat` compatibility flag. Without it, the Worker fails as soon as it imports the SDK.
- **Vercel Functions:** use the Node.js runtime. The Edge runtime isn’t supported, because it has no `node:crypto`.

In a Worker, read the key from the `env` binding instead of `process.env`, and store it with `wrangler secret put PRONTUNO_SENDING_KEY`:

```typescript
import { Prontuno } from "prontuno";

interface Env {
    PRONTUNO_SENDING_KEY: string;
}

export default {
    async fetch(request: Request, env: Env): Promise<Response> {
        const prontuno = new Prontuno({ sendingKey: env.PRONTUNO_SENDING_KEY });

        const result = await prontuno.emails.send({
            from: "Acme <hello@example.com>",
            to: "ada@example.com",
            subject: "Hello from a Worker",
            text: "Sent with the Prontuno Node.js SDK.",
        });

        return Response.json(result.emails);
    },
};
```

## Next steps

[Verify webhooks with an SDKVerify Prontuno webhook signatures and handle delivery, bounce, and complaint events with the official Node.js, PHP, and Laravel SDKs.](https://prontuno.com/docs/integrations/verify-webhooks-with-an-sdk/)

[SDK referenceConfiguration options, methods for every resource, API key routing, errors, and webhook helpers in the Prontuno Node.js, PHP, and Laravel SDKs.](https://prontuno.com/docs/integrations/sdk-reference/)

[The Node.js SDK on GitHubSource code, issues, and releases on GitHub.](https://github.com/prontuno/prontuno-node)
