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

# Senders, per-send domain and tenants

> Manage each customer domain senders via API, send every email from the right domain, and tag it with your tenant for stats and rebilling

## Overview

When your SaaS runs on **one Minimo workspace** with your customers (for example restaurants) as **tenants** inside it, each customer sends from **their own domain**. After a domain is connected and verified (via [Embedded domain connect](https://docs.minimo.it/guides/embedded-domain-connect.md) or `POST /public/v1/domains`), three things remain:

1. **Senders**: register the addresses each domain sends as (`prenotazioni@…`, `noreply@…`).
2. **Per-send domain**: choose, on every API send, which sender or domain the email goes out from.
3. **Tenant tag**: record which of your customers a send belongs to, so you can read stats per customer and rebill.

The chosen domain is the one that signs **DKIM** and receives bounces (**Return-Path** on `bounce.<domain>`), so each customer's mail authenticates as their own.

## Before you start

* An API key with the **Domains** permission (`domains`) for senders, and the **Transactional** permission (`transactional`) for sending. One key can have both. Keep it on your server.
* At least one **verified** domain in your workspace (`status: "verified"` in `GET /public/v1/domains`).

## 1. Manage senders

All routes are scoped to one domain (`{id}` from `GET /public/v1/domains`). An address must live under that exact domain, and the domain must be verified.

```bash theme={null}
# List the senders of domain 812
curl https://api.minimo.it/public/v1/domains/812/senders \
  -H "Authorization: Bearer mn-<clientId>-<secret>"

# Add one
curl -X POST https://api.minimo.it/public/v1/domains/812/senders \
  -H "Authorization: Bearer mn-<clientId>-<secret>" \
  -H "Content-Type: application/json" \
  -d '{"email": "prenotazioni@trattoriadamario.it", "displayName": "Trattoria Da Mario"}'
```

```json theme={null}
{
  "data": {
    "id": 34,
    "email": "prenotazioni@trattoriadamario.it",
    "displayName": "Trattoria Da Mario",
    "domainId": 812,
    "domain": "trattoriadamario.it",
    "verified": true
  }
}
```

| Route | What it does |
| - | - |
| `GET /public/v1/domains/{id}/senders` | Lists the domain's senders with their stable `id`. |
| `POST /public/v1/domains/{id}/senders` | Adds `{ email, displayName? }`. `409` if the address already exists. |
| `PATCH /public/v1/domains/{id}/senders/{senderId}` | Changes `email` (same domain) and/or `displayName`. |
| `DELETE /public/v1/domains/{id}/senders/{senderId}` | Removes the sender. `204`, or `404` if it does not exist. |

`422` when the address is not under that domain (`SENDER_NOT_ON_DOMAIN`) or the domain is not verified yet (`SENDER_DOMAIN_NOT_VERIFIED`). A domain of another workspace is `404`. To move a sender to another domain, delete it and create it under the other one.

> [!NOTE]
>
> A verified domain already has a default sender, created at verification: `info@` for a transactional domain, `hello@`
> for a marketing one.

## 2. Choose the sender on each send

These endpoints accept the sender per call:

* `POST /public/v1/emails` (one-shot HTML)
* `POST /public/v1/transactionals` and `POST /public/v1/templates/email/send` (templates)
* `POST /public/v1/campaigns/{id}/send`

Use **one** of:

| Field | Example | Notes |
| - | - | - |
| `senderId` | `34` | A sender from step 1. Its display name is used unless you pass `fromName`. |
| `from` | `"Trattoria Da Mario <prenotazioni@trattoriadamario.it>"` | Address with an optional name. Any address under a verified domain of your workspace. |
| `fromAddress` | `"prenotazioni@trattoriadamario.it"` | Same rule as `from`, address only (with `fromName` for the name). |

```bash theme={null}
curl -X POST https://api.minimo.it/public/v1/emails \
  -H "Authorization: Bearer mn-<clientId>-<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "guest@example.com",
    "subject": "Your table is confirmed",
    "html": "<p>See you on Friday at 20:30.</p>",
    "senderId": 34,
    "tenant": "restaurant-4812"
  }'
```

**Never a silent fallback.** If the sender does not belong to a verified domain of your workspace, the send is refused with `422` and nothing goes out:

| Code | Meaning |
| - | - |
| `SENDER_NOT_ALLOWED` | `from` / `fromAddress` is not under a verified domain of your workspace. |
| `SENDER_NOT_FOUND` | No sender with that `senderId` in your workspace. |
| `SENDER_DOMAIN_NOT_VERIFIED` | The sender's domain is not verified yet. |
| `SENDER_PARAM_CONFLICT` | More than one of `senderId`, `from`, `fromAddress`. |
| `INVALID_SENDER_ADDRESS` | The address is not a valid email. |
| `INVALID_REPLY_TO` | `replyTo` is not a single valid address. |

Without any of these fields, nothing changes: the template's sender or the workspace default is used, exactly as before.

> [!TIP]
>
> For a marketing campaign, a workspace with a verified **marketing** domain (for example `news.<domain>`) sends from it, for reputation isolation.

### Customers without a domain: a subdomain of yours

Verify **your own** domain once (for example `yoursaas.com`). Then `from` / `fromAddress` can use any **subdomain** of it with no DNS of its own, one per customer:

```json theme={null}
{ "from": "Trattoria Da Mario <bookings@trattoriadamario.yoursaas.com>" }
```

The email is signed with your domain's DKIM and bounces go to `bounce.yoursaas.com`, so it authenticates (SPF, DKIM, DMARC) without any record for `trattoriadamario.yoursaas.com`. A subdomain that is registered as a domain of its own (in any workspace, verified or not) is refused with `SENDER_NOT_ALLOWED`: connect and verify it like any domain instead.

### Replies to your customer's mailbox

Add `replyTo` to `POST /public/v1/transactionals` or `POST /public/v1/templates/email/send` to choose where replies go, for example your customer's own Gmail. Any mailbox, `"Name <address>"` or a bare address:

```json theme={null}
{ "from": "Trattoria Da Mario <bookings@trattoriadamario.yoursaas.com>",
  "replyTo": "Trattoria Da Mario <trattoriadamario@gmail.com>" }
```

Without it, the Reply-To is your workspace's inbox address when it is set as reply-to, otherwise none. A malformed value is refused with `422 INVALID_REPLY_TO`.

## 3. Tag the send with your tenant

Add `tenant` to `POST /public/v1/emails`, `/public/v1/transactionals` or `/public/v1/templates/email/send`: your own reference for the customer, up to 64 characters among letters, digits, `.`, `_`, `:` and `-`, starting with a letter or digit. It is stored on the message.

Read it back:

```bash theme={null}
# Every message of one tenant (newest first)
curl "https://api.minimo.it/public/v1/messages/list?tenant=restaurant-4812" \
  -H "Authorization: Bearer mn-<clientId>-<secret>"
```

Each item carries `tenant`, and `GET /public/v1/emails/{id}/stats` returns it too. Count the items per tenant to meter and rebill your customers.

## Putting it together

1. Customer connects `trattoriadamario.it` in the popup → `domain.verified`.
2. Your backend adds `prenotazioni@trattoriadamario.it` → keeps the returned `id` with the customer.
3. Every email for that customer is sent with `senderId` and `tenant`.
4. At month end, `GET /public/v1/messages/list?tenant=…` per customer.


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