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

# Domains

> Add custom domains to tenants and retrieve their DNS configuration records.

## Overview

A domain is a custom sending domain added to a Microsoft 365 tenant. Microsoft imposes no hard limit on custom domains per tenant. TenantCore recommends 12 domains per tenant as the optimal structure for cold email infrastructure.

TenantCore generates all DNS records required to configure the domain at your registrar and queries live DNS status on each domain so you can monitor authentication health across your infrastructure.

The standard TenantCore structure is:

```plaintext theme={null}
1 tenant
└── 12 domains
    └── 3 mailboxes each = 36 mailboxes total
```

<Note>
  Domains must be verified in Microsoft 365 before mailboxes can be provisioned on them. Add the domain first, configure the DNS records at your registrar, then verify via the TenantCore app.
</Note>

***

## List domains

Returns all custom domains registered on a tenant, including live DNS health status.

```
GET /v1/tenants/{tenant_id}/domains
```

**Path parameters**

| Parameter   | Type   | Description               |
| ----------- | ------ | ------------------------- |
| `tenant_id` | string | The Microsoft tenant GUID |

**Example request**

```bash theme={null}
curl https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains \
  -H "Authorization: Bearer tc_live_your_key"
```

**Example response**

```json theme={null}
{
  "tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "domains": [
    {
      "id": "uuid",
      "domain_name": "mail.acmecorp.com",
      "is_default": true,
      "is_verified": true,
      "spf_status": "v=spf1 include:spf.protection.outlook.com -all",
      "dkim_status": "healthy",
      "dmarc_status": "v=DMARC1; p=quarantine; rua=mailto:dmarc@acmecorp.com",
      "mx_status": "acmecorp-com.mail.protection.outlook.com.",
      "last_checked": "2025-04-20T08:00:00Z"
    }
  ],
  "count": 1
}
```

**Response fields**

| Field          | Type      | Description                                               |
| -------------- | --------- | --------------------------------------------------------- |
| `domain_name`  | string    | The custom domain                                         |
| `is_default`   | boolean   | Whether this is the tenant's primary domain               |
| `is_verified`  | boolean   | Whether Microsoft has verified this domain                |
| `spf_status`   | string    | Raw SPF TXT record, or `"missing"`                        |
| `dkim_status`  | string    | `"healthy"`, `"partial"`, `"missing"`, or selector detail |
| `dmarc_status` | string    | Raw DMARC TXT record, or `"missing"`                      |
| `mx_status`    | string    | Raw MX record, or `"missing"`                             |
| `last_checked` | timestamp | When DNS was last queried                                 |

**Errors**

| Status | Code               | Description                                             |
| ------ | ------------------ | ------------------------------------------------------- |
| `404`  | `tenant_not_found` | Tenant does not exist or belongs to a different account |

***

## Add domain

Adds a custom domain to a tenant in Microsoft 365 and returns the DNS records you need to configure at your registrar. The domain is created in Microsoft but remains unverified until you add the required DNS records and trigger verification.

```
POST /v1/tenants/{tenant_id}/domains
```

**Path parameters**

| Parameter   | Type   | Description               |
| ----------- | ------ | ------------------------- |
| `tenant_id` | string | The Microsoft tenant GUID |

**Request body**

```json theme={null}
{
  "domain": "mail.acmecorp.com"
}
```

| Field    | Type   | Required | Description                                                                                   |
| -------- | ------ | -------- | --------------------------------------------------------------------------------------------- |
| `domain` | string | Yes      | The custom domain to add. Must be a valid domain. `.onmicrosoft.com` domains are not allowed. |

**Example request**

```bash theme={null}
curl -X POST \
  https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains \
  -H "Authorization: Bearer tc_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"domain": "mail.acmecorp.com"}'
```

**Example response**

```json theme={null}
{
  "status": "success",
  "tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "domain": {
    "domain_name": "mail.acmecorp.com",
    "is_default": false,
    "is_verified": false
  },
  "verification_dns_records": [
    {
      "recordType": "domainDnsTxtRecord",
      "label": "@",
      "text": "MS=ms12345678",
      "ttl": 3600
    }
  ],
  "service_configuration_records": [
    {
      "recordType": "domainDnsMxRecord",
      "label": "@",
      "mailExchange": "mail.acmecorp.com.mail.protection.outlook.com",
      "preference": 0,
      "ttl": 3600
    }
  ],
  "live_dns_status": {
    "spf_status": "missing",
    "dkim_status": "missing",
    "dmarc_status": "missing",
    "mx_status": "missing"
  },
  "dkim_verification_state": {
    "status": "missing",
    "message": "missing (selector1, selector2)"
  }
}
```

**After adding a domain**

1. Add the `verification_dns_records` TXT record at your registrar
2. Add all `service_configuration_records` (MX, CNAME for DKIM, TXT for SPF)
3. Wait for DNS propagation (typically 5 to 30 minutes, up to 48 hours)
4. Trigger domain verification from the TenantCore app

**Errors**

| Status | Code                   | Description                                             |
| ------ | ---------------------- | ------------------------------------------------------- |
| `400`  | `domain_limit_reached` | Tenant already has 12 domains                           |
| `400`  | —                      | Invalid domain format or `.onmicrosoft.com` domain      |
| `404`  | `tenant_not_found`     | Tenant does not exist or belongs to a different account |

***

## Verify domain

Triggers Microsoft domain verification after DNS records have been configured. This is required before mailboxes can be created and sending can begin.

```

POST /v1/tenants/{tenant_id}/domains/{domain_name}/verify

```

**Path parameters**

| Parameter     | Type   | Description               |
| ------------- | ------ | ------------------------- |
| `tenant_id`   | string | The Microsoft tenant GUID |
| `domain_name` | string | The custom domain name    |

**Example request**

```bash theme={null}
curl -X POST \
  https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains/mail.acmecorp.com/verify \
  -H "Authorization: Bearer tc_live_your_key"
```

**Example response**

```json theme={null}
{
  "status": "success",
  "domain_name": "mail.acmecorp.com",
  "is_verified": true,
  "message": "Domain successfully verified in Microsoft 365"
}
```

**Errors**

| Status | Code                  | Description                                 |
| ------ | --------------------- | ------------------------------------------- |
| `400`  | `verification_failed` | DNS records not yet propagated or incorrect |
| `404`  | `domain_not_found`    | Domain is not registered on this tenant     |

***

## Enable DKIM

Enables DKIM signing for a verified domain. This is required for full email authentication and deliverability.

```
POST /v1/tenants/{tenant_id}/domains/{domain_name}/enable-dkim
```

**Path parameters**

| Parameter     | Type   | Description               |
| ------------- | ------ | ------------------------- |
| `tenant_id`   | string | The Microsoft tenant GUID |
| `domain_name` | string | The custom domain name    |

**Example request**

```bash theme={null}
curl -X POST \
  https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains/mail.acmecorp.com/enable-dkim \
  -H "Authorization: Bearer tc_live_your_key"
```

**Example response**

```json theme={null}
{
  "status": "success",
  "domain_name": "mail.acmecorp.com",
  "dkim_status": "enabled",
  "message": "DKIM successfully enabled"
}
```

**Errors**

| Status | Code               | Description                             |
| ------ | ------------------ | --------------------------------------- |
| `400`  | `dkim_not_ready`   | Required CNAME records not found in DNS |
| `404`  | `domain_not_found` | Domain is not registered on this tenant |

***

## Get DKIM status

Returns the current DKIM state for a domain. Useful for polling until DNS propagation is complete and DKIM can be enabled.

```
GET /v1/tenants/{tenant_id}/domains/{domain_name}/dkim-status
```

**Path parameters**

| Parameter     | Type   | Description               |
| ------------- | ------ | ------------------------- |
| `tenant_id`   | string | The Microsoft tenant GUID |
| `domain_name` | string | The custom domain name    |

**Example request**

```bash theme={null}
curl https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains/mail.acmecorp.com/dkim-status \
  -H "Authorization: Bearer tc_live_your_key"
```

**Example response**

```json theme={null}
{
  "domain_name": "mail.acmecorp.com",
  "dkim_status": "pending",
  "selectors": {
    "selector1": "missing",
    "selector2": "propagated"
  },
  "ready_to_enable": false,
  "last_checked": "2025-04-20T08:00:00Z"
}
```

**Response fields**

| Field             | Type      | Description                         |
| ----------------- | --------- | ----------------------------------- |
| `dkim_status`     | string    | `"pending"`, `"ready"`, `"enabled"` |
| `selectors`       | object    | Status of selector1 and selector2   |
| `ready_to_enable` | boolean   | Whether DKIM can be enabled         |
| `last_checked`    | timestamp | Last DNS check                      |

***

## Get DNS records

Returns the stored DNS records and current DNS health for a specific domain. This does not trigger a live DNS re-check — it returns what was last recorded. Use the TenantCore app to trigger a fresh DNS check.

```
GET /v1/tenants/{tenant_id}/domains/{domain_name}/dns
```

**Path parameters**

| Parameter     | Type   | Description                                      |
| ------------- | ------ | ------------------------------------------------ |
| `tenant_id`   | string | The Microsoft tenant GUID                        |
| `domain_name` | string | The custom domain name, e.g. `mail.acmecorp.com` |

**Example request**

```bash theme={null}
curl https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains/mail.acmecorp.com/dns \
  -H "Authorization: Bearer tc_live_your_key"
```

**Example response**

```json theme={null}
{
  "tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "domain_name": "mail.acmecorp.com",
  "is_default": false,
  "is_verified": true,
  "dns_status": {
    "spf": "v=spf1 include:spf.protection.outlook.com -all",
    "dkim": "healthy",
    "dmarc": "v=DMARC1; p=quarantine",
    "mx": "mail.acmecorp.com.mail.protection.outlook.com."
  },
  "verification_dns_records": [],
  "service_configuration_records": [
    {
      "recordType": "domainDnsMxRecord",
      "label": "@",
      "mailExchange": "mail.acmecorp.com.mail.protection.outlook.com",
      "preference": 0,
      "ttl": 3600
    },
    {
      "recordType": "domainDnsCnameRecord",
      "label": "selector1._domainkey",
      "canonicalName": "selector1-mail-acmecorp-com._domainkey.acmecorp.onmicrosoft.com",
      "ttl": 3600
    },
    {
      "recordType": "domainDnsCnameRecord",
      "label": "selector2._domainkey",
      "canonicalName": "selector2-mail-acmecorp-com._domainkey.acmecorp.onmicrosoft.com",
      "ttl": 3600
    }
  ],
  "last_checked": "2025-04-20T08:00:00Z"
}
```

**Errors**

| Status | Code               | Description                                             |
| ------ | ------------------ | ------------------------------------------------------- |
| `404`  | `tenant_not_found` | Tenant does not exist or belongs to a different account |
| `404`  | `domain_not_found` | Domain is not registered on this tenant                 |
