# Senders

A sender is a saved From: the name people see, an address on one of your verified domains, an optional reply-to address, and a short handle. Send with `sender: "billing"` instead of writing the address in every call, give each template the sender it goes out as, and pick a sender on each automation email. Change a sender once and every automation email and template using it picks up the change.

In the dashboard: **Domains › Senders**.

Base URL: `https://api.groundmail.dev`

Auth:

```http
Authorization: Bearer gm_...
```

Reading senders needs the `domains:read` scope; creating, changing and deleting them needs `domains:write`.

---

## Send as a sender

Pass `sender` (a handle or id) on [`POST /emails`](https://docs.groundmail.dev/emails) or each item of a [batch](https://docs.groundmail.dev/emails#batch-send):

```ts
await groundmail.emails.send({
  sender: 'billing',
  to: user.email,
  template: { id: 'receipt', variables: { amount: '$20.00' } },
})
```

The email goes out as the sender's name and address, and replies go to its reply-to address unless the request sets `reply_to`.

Who an email is from, in order:

1. `from` (and `reply_to`) on the request.
2. `sender` on the request.
3. The template's sender, when you send a template that has one.
4. Your default sender.

So with a default sender, or a sender on the template, a send can leave out `from` entirely. A `from` you pass always wins. A `sender` your team doesn't have returns `422` with a `validation_error`.

---

## The sender object

```json
{
  "id": "4f0c7e2a-6b1d-4c55-9a3e-2d8f6a1b9c10",
  "name": "Acme Billing",
  "address": "billing@mail.acme.com",
  "from": "Acme Billing <billing@mail.acme.com>",
  "reply_to": "billing@acme.com",
  "handle": "billing",
  "is_default": false,
  "used_by": { "automations": 2, "templates": 3 },
  "created_at": "2026-11-02T10:00:00.000Z",
  "updated_at": "2026-11-02T10:00:00.000Z"
}
```

| Field | Notes |
|-------|-------|
| `name` | The name people see in their inbox. Up to 120 characters |
| `address` | The address it sends from. Must be on one of your verified domains |
| `from` | Name and address as the inbox shows them |
| `reply_to` | Where replies go. `null` means replies go to `address` |
| `handle` | What sends use (`sender: "billing"`). Lowercase letters, digits and hyphens, unique on your team |
| `is_default` | Sends with no `from`, `sender` or template sender go out as the default, and new automations start with it. At most one per team |
| `used_by` | How many automations and templates send as it |

---

## List senders

`GET /senders`

```bash
curl https://api.groundmail.dev/senders \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY"
```

```json
{ "data": [ { "id": "…", "handle": "default", "is_default": true, "...": "…" } ] }
```

The default sender comes first.

---

## Add a sender

`POST /senders`

```bash
curl -X POST https://api.groundmail.dev/senders \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme Billing", "address": "billing@mail.acme.com", "reply_to": "billing@acme.com", "handle": "billing" }'
```

| Field | Required | Notes |
|-------|----------|-------|
| `name` | Yes | The name people see |
| `address` | Yes | On one of your verified domains. [Add and verify a domain](https://docs.groundmail.dev/domains) first to use a new one |
| `reply_to` | No | Leave it out to get replies at `address`. Use an inbox someone reads |
| `handle` | No | Made from `name` when left out (`Acme Billing` → `acme-billing`) |
| `is_default` | No | Make it the default. Your first sender becomes the default unless this is `false`; making another sender the default takes it from the previous one |

Returns `201` with `{ "sender": { … } }`. A handle another sender already uses returns `409` `conflict`; an address on a domain that isn't verified returns `400` `validation_error`.

---

## Retrieve, update, delete

- `GET /senders/{id}`: id or handle. Returns `{ "sender": { … } }`.
- `PATCH /senders/{id}`: any of `name`, `address`, `reply_to` (`null` sends replies to `address`), `handle`, `is_default`. Automation emails using the sender pick up the change on their next send. Renaming `handle` updates the automation emails that use it; code that sends with the old handle needs the new one.
- `DELETE /senders/{id}`: returns `{ "ok": true }`.

A sender that an automation email or a template uses can't be deleted. The `409` names them, and `details` lists them, so you can pick another sender on each first:

```json
{
  "error": {
    "message": "Sender \"billing\" is used by 1 automation (Receipts) and 1 template (Receipt). Pick another sender on them, then delete it.",
    "code": "conflict",
    "details": { "automations": ["Receipts"], "templates": ["Receipt"] }
  }
}
```

Deleting the default sender is allowed: sends with no `from` then need one again.

---

## Templates and automations

- **Templates:** set `sender_id` (a sender id or handle) on [create or update](https://docs.groundmail.dev/templates#model), or pick **Sends as** in the template editor. Sends of that template without `from` or `sender` go out as it, and so does the editor's **Send a test**.
- **Automations:** an email step can carry `email.sender` (a handle) instead of `email.from`. It's looked up each time the step sends. See [Who an email step is from](https://docs.groundmail.dev/automations#who-an-email-step-is-from).

---

## SDK

```ts
import { Groundmail } from 'groundmail'

const groundmail = new Groundmail(process.env.GROUNDMAIL_API_KEY!)

await groundmail.senders.create({
  name: 'Acme Billing',
  address: 'billing@mail.acme.com',
  reply_to: 'billing@acme.com',
  handle: 'billing',
})

const { data } = await groundmail.senders.list()
await groundmail.senders.update('billing', { reply_to: 'accounts@acme.com' })
await groundmail.senders.remove('billing')
```

---

## CLI

```bash
groundmail senders list
groundmail senders create --name "Acme Billing" --address billing@mail.acme.com --reply-to billing@acme.com --handle billing
groundmail senders get billing
groundmail senders update billing --default
groundmail senders delete billing --yes

groundmail emails send --sender billing --to user@example.com --subject "Your receipt" --html "<p>Thanks!</p>"
groundmail templates update --id receipt --sender billing
```

---

## OpenAPI

Full schemas: [openapi.yaml](https://docs.groundmail.dev/openapi.yaml) (`Senders` tag).
