# Recipe: Microsoft 365 on a subdomain

Set up Microsoft 365 mail on a host such as `advisor.example.com`. The flow is the same as [Google Workspace](https://maildns.io/docs/google-workspace.md): add the verification record, wait until it is live, verify, then add the mail records. Everything goes on the host, never on the root domain. The examples are Dynadot style; any of the six [API styles](https://maildns.io/docs.md#api-styles) works the same way.

## The records

Microsoft shows the exact values for your tenant in the **Microsoft 365 admin center** under **Settings > Domains** (and DKIM in the Microsoft Defender portal, under email authentication settings). Copy them from there; the values below only show their shape.

| Step | Record (host form) | Value |
|---|---|---|
| 1. Verify the domain | `TXT @` | `MS=ms12345678` |
| 2. Mail | `MX @`, priority 0 | The value Microsoft shows, ending in `mail.protection.outlook.com` (for example `advisor-example-com.mail.protection.outlook.com`) |
| 3. SPF | `TXT @` | `v=spf1 include:spf.protection.outlook.com -all` |
| 4. Outlook setup | `CNAME autodiscover` | `autodiscover.outlook.com` |
| 5. DKIM | `CNAME selector1._domainkey` and `CNAME selector2._domainkey` | The two targets Microsoft shows |
| 6. DMARC | `TXT _dmarc` | `v=DMARC1; p=quarantine` |

## 1. Verify

```bash
curl https://api.maildns.io/api3.json \
  --data-urlencode key=KEY \
  --data-urlencode command=set_dns2 \
  --data-urlencode domain=advisor.example.com \
  --data-urlencode add_dns_to_current_setting=1 \
  --data-urlencode main_record_type0=txt \
  --data-urlencode main_record0=MS=ms12345678
```

Poll [`dns_status`](https://maildns.io/docs/propagation.md) with `subdomain=@`, `record_type=txt`, `value=MS=ms12345678` until `Live` is `yes`, then press **Verify** in the admin center.

## 2 to 4, and 6. Mail records

```bash
curl https://api.maildns.io/api3.json \
  --data-urlencode key=KEY \
  --data-urlencode command=set_dns2 \
  --data-urlencode domain=advisor.example.com \
  --data-urlencode add_dns_to_current_setting=1 \
  --data-urlencode main_record_type0=mx \
  --data-urlencode main_record0=advisor-example-com.mail.protection.outlook.com \
  --data-urlencode main_recordx0=0 \
  --data-urlencode main_record_type1=txt \
  --data-urlencode 'main_record1=v=spf1 include:spf.protection.outlook.com -all' \
  --data-urlencode subdomain0=autodiscover \
  --data-urlencode sub_record_type0=cname \
  --data-urlencode sub_record0=autodiscover.outlook.com \
  --data-urlencode subdomain1=_dmarc \
  --data-urlencode sub_record_type1=txt \
  --data-urlencode 'sub_record1=v=DMARC1; p=quarantine'
```

## 5. DKIM

Add the two CNAMEs with the targets Microsoft shows (`selector1-...` and `selector2-...`), wait until both are live, then turn DKIM signing on in the Defender portal:

```bash
curl https://api.maildns.io/api3.json \
  --data-urlencode key=KEY \
  --data-urlencode command=set_dns2 \
  --data-urlencode domain=advisor.example.com \
  --data-urlencode add_dns_to_current_setting=1 \
  --data-urlencode subdomain0=selector1._domainkey \
  --data-urlencode sub_record_type0=cname \
  --data-urlencode sub_record0=SELECTOR1_TARGET \
  --data-urlencode subdomain1=selector2._domainkey \
  --data-urlencode sub_record_type1=cname \
  --data-urlencode sub_record1=SELECTOR2_TARGET
```

Replace `SELECTOR1_TARGET` and `SELECTOR2_TARGET` with the two values from the Defender portal.

## Things to watch

- One SPF record and one DMARC record per name. If the host already has one, merge instead of adding a second.
- A CNAME can't share its name with other records, so `autodiscover` and the two `_domainkey` names must have nothing else on them.
- The [Python script](https://maildns.io/docs/google-workspace.md#many-hosts-a-complete-script) in the Google recipe adapts easily: change the values in `verify` and `mail`, and make `dkim` add the two CNAMEs.
