> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiveku.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sending Emails

> Send transactional emails via SDK, REST API, or SMTP

Hiveku offers three ways to send email. Use the SDK when you can, fall back to REST when you cannot, and use SMTP when you need to plug Hiveku into a third-party service that only speaks SMTP (Supabase Auth, Firebase Auth, WordPress, etc.).

## Choose an integration

<Tabs>
  <Tab title="SDK (recommended)">
    The `@hiveku-apps/email` SDK handles authentication, retries, rate-limit backoff, and typed responses.

    ```bash theme={null}
    npm install @hiveku-apps/email
    ```

    ```ts theme={null}
    import { Hiveku } from "@hiveku-apps/email";

    const hiveku = new Hiveku({ apiKey: process.env.HIVEKU_API_KEY! });

    const { messageId } = await hiveku.emails.send({
      from: "Acme <noreply@mail.acme.com>",
      to: "customer@example.com",
      subject: "Your order #1024 has shipped",
      html: "<h1>On the way!</h1><p>Tracking: ABC123</p>",
      text: "On the way! Tracking: ABC123",
      tags: ["order-shipped", "tenant-42"],
    });
    ```
  </Tab>

  <Tab title="REST API">
    POST to `/v1/email/send` with a Bearer token.

    ```bash theme={null}
    curl -X POST https://api.hiveku.com/v1/email/send \
      -H "Authorization: Bearer hk_live_xxxxxxxxxxxxxxxxxxxx" \
      -H "Content-Type: application/json" \
      -d '{
        "from": "Acme <noreply@mail.acme.com>",
        "to": "customer@example.com",
        "subject": "Your order #1024 has shipped",
        "html": "<h1>On the way!</h1><p>Tracking: ABC123</p>",
        "text": "On the way! Tracking: ABC123",
        "tags": ["order-shipped", "tenant-42"]
      }'
    ```

    Response:

    ```json theme={null}
    {
      "messageId": "msg_01HPQRS...",
      "status": "queued"
    }
    ```
  </Tab>

  <Tab title="SMTP">
    Use SMTP for third-party services that cannot call an HTTP API (Supabase Auth, Firebase Auth, WordPress, Rails `ActionMailer`, Django `EMAIL_BACKEND`, etc.).

    | Setting  | Value                                    |
    | -------- | ---------------------------------------- |
    | Host     | `smtp.hiveku.com`                        |
    | Port     | `465` (SSL) or `587` / `2587` (STARTTLS) |
    | Username | `apikey`                                 |
    | Password | Your `hk_live_...` or `hk_test_...` key  |
    | Auth     | `PLAIN` or `LOGIN`                       |

    SMTP sends inherit the same domain verification, rate limits, and scopes as the API key used as the password.
  </Tab>
</Tabs>

## Email parameters

<ParamField path="to" type="string | string[]" required>
  Recipient address(es). Pass a string for one recipient or an array for many.
</ParamField>

<ParamField path="from" type="string" required>
  Sender address. Must be on a verified domain. Supports friendly format: `Acme <noreply@mail.acme.com>`.
</ParamField>

<ParamField path="subject" type="string" required>
  Email subject line.
</ParamField>

<ParamField path="html" type="string">
  HTML body. At least one of `html` or `text` is required.
</ParamField>

<ParamField path="text" type="string">
  Plain-text body. Strongly recommended alongside `html` for deliverability.
</ParamField>

<ParamField path="cc" type="string | string[]">
  CC recipients.
</ParamField>

<ParamField path="bcc" type="string | string[]">
  BCC recipients.
</ParamField>

<ParamField path="reply_to" type="string | string[]">
  Reply-To address(es).
</ParamField>

<ParamField path="attachments" type="Attachment[]">
  File attachments. See [Attachments](#attachments) below.
</ParamField>

<ParamField path="tags" type="string[]">
  Tags for filtering analytics and webhooks. Up to 10 tags per email.
</ParamField>

<ParamField path="headers" type="Record<string, string>">
  Custom headers (e.g., `List-Unsubscribe`).
</ParamField>

<ParamField path="template_id" type="string">
  Send with a template instead of raw `html`/`text`. See [Templates](/email/templates).
</ParamField>

<ParamField path="variables" type="Record<string, unknown>">
  Variable values for the template.
</ParamField>

<ParamField path="scheduled_at" type="string (ISO 8601)">
  Schedule the send for a future time.
</ParamField>

<ParamField path="idempotency_key" type="string">
  Client-generated key to prevent duplicate sends on retry.
</ParamField>

## Batch sending

Send up to **100 emails** in a single request with `POST /v1/email/batch`:

```ts theme={null}
const { results } = await hiveku.emails.batch([
  {
    from: "noreply@mail.acme.com",
    to: "alice@example.com",
    subject: "Welcome, Alice",
    html: "<p>Hi Alice!</p>",
  },
  {
    from: "noreply@mail.acme.com",
    to: "bob@example.com",
    subject: "Welcome, Bob",
    html: "<p>Hi Bob!</p>",
  },
]);

for (const r of results) {
  console.log(r.messageId, r.status);
}
```

Each item in the batch is validated and enqueued independently — a failure on one item does not block the others.

## Template sending

If you have a saved template, reference it by ID and pass variables:

```ts theme={null}
await hiveku.emails.sendTemplate({
  template_id: "tmpl_order_shipped",
  from: "noreply@mail.acme.com",
  to: "customer@example.com",
  variables: {
    customer_name: "Sam",
    order_number: "1024",
    tracking_url: "https://acme.com/track/ABC123",
  },
});
```

REST endpoint: `POST /v1/email/send-template`.

## Scheduled delivery

Schedule a send for a future time by passing an ISO 8601 `scheduled_at`:

```ts theme={null}
await hiveku.emails.send({
  from: "noreply@mail.acme.com",
  to: "customer@example.com",
  subject: "Your trial ends tomorrow",
  html: "<p>Reminder!</p>",
  scheduled_at: "2026-04-18T15:00:00Z",
});
```

Scheduled emails can be canceled before their delivery window opens.

## Attachments

Attachments are passed as base64-encoded content:

```ts theme={null}
await hiveku.emails.send({
  from: "noreply@mail.acme.com",
  to: "customer@example.com",
  subject: "Your invoice",
  html: "<p>See attached.</p>",
  attachments: [
    {
      filename: "invoice-1024.pdf",
      content: pdfBuffer.toString("base64"),
      content_type: "application/pdf",
    },
  ],
});
```

Total message size (including all attachments) is capped at 40 MB.

## Custom headers

Pass arbitrary headers via the `headers` field. Common uses:

```ts theme={null}
await hiveku.emails.send({
  from: "newsletter@mail.acme.com",
  to: "subscriber@example.com",
  subject: "This week at Acme",
  html: "<p>News!</p>",
  headers: {
    "List-Unsubscribe": "<https://acme.com/unsub?u=42>, <mailto:unsub@acme.com>",
    "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
  },
});
```

## Tracking tags

Tags flow through to analytics, webhooks, and suppression filters. Use them to slice sends by feature, tenant, or campaign:

```ts theme={null}
tags: ["password-reset", "tenant-42", "experiment-b"];
```

## Idempotency

Pass an `idempotency_key` to make retries safe. If Hiveku receives the same key twice within 24 hours, the second request returns the original response without sending again.

```ts theme={null}
await hiveku.emails.send({
  idempotency_key: `order-${orderId}-shipped`,
  from: "noreply@mail.acme.com",
  to: "customer@example.com",
  subject: "Your order shipped",
  html: "<p>On the way!</p>",
});
```

## Response format

All send endpoints return a consistent shape:

```json theme={null}
{
  "messageId": "msg_01HPQRS...",
  "status": "queued",
  "scheduled_at": null,
  "created_at": "2026-04-17T12:34:56Z"
}
```

Track the message further via [Webhooks](/email/webhooks) or the dashboard logs.
