---
title: "Send marketing email"
description: "Group contacts into segments and send them a broadcast, from server code or an admin screen, never from a public function."
canonical_url: "https://nuxt-backend.dev/docs/email/engagement/marketing"
---
# Send marketing email

> Group contacts into segments and send them a broadcast, from server code or an admin screen, never from a public function.

Marketing email goes through the provider's segments, contacts and broadcasts. `email.segments`, `email.contacts` and `email.broadcasts` wrap its SDK with `EMAIL_API_KEY`:

- **email.segments**: `create`, `list`, `remove`, plus `addContact` and `removeContact` for a contact that already exists. A segment is the group a broadcast goes to.
- **email.contacts**: `add`, `list`, `update`, `remove`. A contact is one record however many segments it sits in: `contacts.add({ email, segments: [{ id }] })` places it at creation, `segments.addContact({ email, segmentId })` later.
- **email.broadcasts**: `create`, then `send(id, payload?)`.

Segments replaced the provider's audiences, which this package does not wrap.

## The scaffolded actions

All of it is privileged: a public function would be an open relay on your verified domain. The scaffolded `backend/email.ts` ships the four steps as `internalAction`s:

```ts [backend/email.ts]
export const createSegment = internalAction({
  args: { name: v.string() },
  handler: async (ctx, { name }) => email.segments.create({ name }),
})
export const addContact = internalAction({
  args: { segmentId: v.string(), email: v.string(), firstName: v.optional(v.string()), lastName: v.optional(v.string()) },
  handler: async (ctx, { segmentId, ...contact }) => email.contacts.add({ ...contact, segments: [{ id: segmentId }] }),
})
export const createBroadcast = internalAction({
  args: { segmentId: v.string(), from: v.string(), subject: v.string(), html: v.string() },
  handler: async (ctx, args) => email.broadcasts.create(args),
})
export const sendBroadcast = internalAction({
  args: { broadcastId: v.string() },
  handler: async (ctx, { broadcastId }) => email.broadcasts.send(broadcastId),
})
```

Run them from the command line or from your own server code:

```bash
npx convex run email:createSegment '{"name":"Launch list"}'
```

To expose one to an admin screen, declare it with the `admin.action` builder from `backend/functions.ts` instead of `internalAction`: see [Protect backend functions](https://nuxt-backend.dev/docs/auth/access/functions).

<note>

Broadcasts need a verified sending domain: the provider refuses one from its onboarding sender, test mode or not.

</note>


## Sitemap

See the full [sitemap](https://nuxt-backend.dev/sitemap.md) for all pages.
