> ## 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.

# Embedded domain connect

> Let your users connect their sending domain from inside your SaaS, in a popup hosted by Minimo

## Overview

If you run a SaaS on top of Minimo (one Minimo workspace for your SaaS, your customers as tenants inside it), each of your customers sends email from **their own domain**. Embedded domain connect lets them set that domain up **without leaving your product and without a Minimo account**:

1. your **backend** asks Minimo for a short-lived popup session for one domain;
2. your **frontend** opens the popup with one line of JavaScript;
3. your customer sees the DNS records (copy or CSV), can configure them in **one click** when their DNS provider supports Domain Connect (Cloudflare, GoDaddy and others), and watches the verification happen live;
4. your page receives **events** (`domain.verified`, …) through `postMessage`.

Your API key never reaches the browser.

> [!NOTE]
>
> The popup has a neutral look with a small "Powered by Minimo" line that you can turn off. It is available in English and Italian.

## Before you start

* An API key with the **Domains** permission (`domains`), kept on your server. See [Authentication](https://docs.minimo.it/api-reference/overview/authentication.md).
* The **origins** of the pages that will open the popup, set once:

```bash theme={null}
curl -X PUT https://api.minimo.it/public/v1/domains/embed-settings \
  -H "Authorization: Bearer mn-<clientId>-<secret>" \
  -H "Content-Type: application/json" \
  -d '{"allowedOrigins": ["https://app.yoursaas.com", "http://localhost:3000"]}'
```

Only these origins can embed the popup in an iframe (`frame-ancestors`), receive its events and be used as `returnUrl`. Exact `scheme://host[:port]` values: `https` only, `http` for `localhost`. No wildcards, at most 20. `GET` on the same path reads the list.

## 1. Create a session (your backend)

```bash theme={null}
curl -X POST https://api.minimo.it/public/v1/domains/embed-sessions \
  -H "Authorization: Bearer mn-<clientId>-<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "trattoriadamario.it",
    "tenant": "restaurant_4812",
    "locale": "it"
  }'
```

| Field | Required | Notes |
| - | - | - |
| `domain` | yes | The domain to connect. Added to your workspace when it is not there yet (same as `POST /public/v1/domains`); an existing domain of your workspace is reused. |
| `role` | no | `transactional` (default) or `marketing`, used when the domain is created. |
| `tenant` | no | Your reference for the customer. Echoed back in every event. |
| `locale` | no | `en` (default) or `it`. |
| `returnUrl` | no | Where "Continue" goes when the popup is opened as a full page. Must be on an allowed origin. |
| `poweredBy` | no | `false` hides the "Powered by Minimo" line. |
| `expiresInMinutes` | no | 5 to 60, default 30. |

Response:

```json theme={null}
{
  "data": {
    "sessionId": "0b8e5c1e-…",
    "url": "https://api.minimo.it/public/v1/embed/domain?token=mde_…",
    "expiresAt": "2026-11-06T10:30:00.000Z",
    "domain": { "id": 812, "domain": "trattoriadamario.it", "status": "pending", "created": true }
  }
}
```

The `url` is **single use**: it can be opened once (a reload of the same tab keeps working). To open the popup again, create a new session. Pass only the `url` to your frontend.

Errors: `400 EMBED_ORIGINS_NOT_CONFIGURED` (no allowed origin yet), `400 RETURN_URL_NOT_ALLOWED`, `409` (the domain belongs to another workspace), `429` (more than 120 sessions in an hour).

## 2. Open the popup (your frontend)

```html theme={null}
<script src="https://api.minimo.it/public/v1/embed/domain-connect.js"></script>
<script>
  document.getElementById('connect-domain').addEventListener('click', async () => {
    const { url } = await fetch('/api/minimo/domain-session', { method: 'POST' }).then((r) => r.json());
    Minimo.openDomainConnect({
      url,
      mode: 'modal', // 'modal' = iframe overlay (default), 'popup' = new window
      onEvent: (event) => {
        if (event.type === 'domain.verified') {
          // mark the customer's domain as ready in your app
        }
      },
    });
  });
</script>
```

`Minimo.openDomainConnect` returns `{ close() }`. In `popup` mode call it from a click handler (browsers block popups opened later); if you fetch the session first, open the window synchronously and let the SDK reuse it, or use `modal`.

## 3. Events

Every event is a `postMessage` to your page, sent **only** to your allowed origins. The SDK checks the sender origin for you and calls `onEvent({ type, sessionId, tenant, data })`.

| `type` | When | `data` |
| - | - | - |
| `ready` | The popup loaded the session | `domainId`, `domain`, `status` |
| `domain.created` | The session created the domain | `domainId`, `domain` |
| `records.updated` | A check found a record in a new state | `domainId`, `status`, `records[]` |
| `domain.verified` | The domain can send | `domainId`, `domain` |
| `closed` | The user closed the popup | `verified` |

Without the SDK, listen yourself and check the origin:

```js theme={null}
window.addEventListener('message', (e) => {
  if (e.origin !== 'https://api.minimo.it' || e.data?.source !== 'minimo-domain-connect') return;
  console.log(e.data.type, e.data.data);
});
```

Events are a convenience for the UI. To know the real state from your backend, call `GET /public/v1/domains/{id}` or `POST /public/v1/domains/{id}/verify`.

## What the user sees

* **The DNS records** to publish (ownership TXT, DKIM CNAMEs, DMARC, bounce MX and SPF), each with a copy button, plus **Download CSV** for whoever manages their DNS.
* **Configure automatically** when the domain's DNS provider supports Domain Connect: the provider opens in its own window, the user approves, and control comes back to the popup, which checks again.
* **Live verification**: the popup checks every 15 seconds and on demand, with a status per record.
* **Domain verified** once the domain can send. The default sender (`info@` for transactional, `hello@` for marketing) is created at that moment.

## Security

* The API key stays on your server; the browser only gets a short-lived, single-use link.
* The link token and the popup's session token are stored hashed. A replayed link reads as "already opened".
* A session can only read and verify **its own** domain in **your** workspace.
* Embedding, events and `returnUrl` are limited to your allowed origins.
* Session creation and popup calls are rate limited.


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