> ## Documentation Index
> Fetch the complete documentation index at: https://help.the-meridian.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Send email from your app

Use the Meridian SDK or public API to send an **active** email template to one or more recipients. Every send consumes your app's monthly email allowance and is tracked in [Sends and deliverability](/email-sends).

<Note>The SDK methods on this page require `@the-meridian/sdk` 1.19.0 or later. The HTTP endpoint works from any version.</Note>

## Recommended integration

The simplest, safest path for merchant-facing mail:

```ts theme={null}
import { createMeridianApp } from "@the-meridian/sdk/server"

const meridian = createMeridianApp()

// Best-effort: null on failure, never throws into your request handler.
await meridian.sendEmail({
  shop: session.shop,
  emailId: "template-uuid",
  to: "owner@store.com",
  variables: { name: "Ada" },
  idempotencyKey: "welcome-123",
})
```

Under the hood this mints a shop token, calls `sendEmail` with shop context (for optional merchant metering), and logs actionable errors without breaking your app.

## Shop context vs app context

| Auth | When to use | Merchant billing |
| - | - | - |
| **Shop token** (`createMeridianServerClient` / `meridian.sendEmail`) | Email to a specific store you already identified | Yes, when you configured a Plan Builder usage event (default `emails_sent`) |
| **API key** (`createMeridianAdminClient`) | Internal/ops mail with no store context | No, platform quota only |

Shop-token sends never fire `usage_event.tracked` automations, so a transactional email cannot accidentally re-enter your automation graph.

`variables` fills the template's `{{tokens}}`. Meridian adds one value itself: [`app_url`](/automation-variables), which links to your app in that store's admin on a shop-token send. An API-key send has no store, so `app_url` is empty there. An `app_url` you pass in `variables` always wins.

## How this differs from automations

| | Automations | SDK/API send |
| - | - | - |
| Trigger | Lifecycle, webhooks, usage events, etc. | Your app code |
| Template | Subject/HTML **snapshot** at save time | Live **active** template from the builder |
| Draft templates | Allowed (snapshot already stored) | Rejected. Activate the template first |
| Merchant billing | Platform quota only | Optional via shop token + Plan Builder event |
| `source` in sends list | `automation` | `api` |

Automations and SDK sends share the same SendGrid pipeline, quota enforcement, and deliverability tracking. They do not double-charge or conflict.

## Setup

1. Create and activate an email template in the Meridian dashboard.
2. Copy the template UUID.
3. Call `sendEmail` from your app backend (never from the browser, because the shop token and API key are secrets).

## Examples

### Shop context (merchant billing)

```ts theme={null}
import { createMeridianServerClient } from "@the-meridian/sdk/server"

const client = createMeridianServerClient(shopToken)
await client.sendEmail({
  emailId: "template-uuid",
  to: "owner@store.com",
  variables: { name: "Ada" },
  idempotencyKey: "welcome-123",
})
```

### App context (platform quota only)

```ts theme={null}
import { createMeridianAdminClient } from "@the-meridian/sdk/server"

const admin = createMeridianAdminClient({ appId, apiKey })
await admin.sendEmail({
  emailId: "template-uuid",
  to: ["ops@example.com"],
})
```

## Reliability

* **Idempotency:** Pass `idempotencyKey` on sends you may retry (webhook handlers, job queues). Retries with the same key, template, and recipient return the original send row instead of dispatching again. Reusing a key for a *different* template returns `IDEMPOTENCY_KEY_CONFLICT`.
* **All-or-nothing per request:** If any recipient in a batch fails at SendGrid, the API returns an error. Quota for failed units is released automatically.
* **Rate limit:** 60 requests/minute per app.

## Limits and errors

* Up to 20 recipients per request.
* Inactive templates are rejected (`422`).
* Over-quota sends return `USAGE_LIMIT_EXCEEDED` (`409`).
* Platform capacity guard returns `SENDGRID_CAPACITY_EXHAUSTED` (`503`).

See [Emails](/emails) for template authoring and [Automations](/automations) for lifecycle sends.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.