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:
1 tenant
└── 12 domains
└── 3 mailboxes each = 36 mailboxes total
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.
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
curl https://api.tenantcore.io/v1/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/domains \
-H "Authorization: Bearer tc_live_your_key"
Example response
{
"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
{
"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
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
{
"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
- Add the
verification_dns_records TXT record at your registrar
- Add all
service_configuration_records (MX, CNAME for DKIM, TXT for SPF)
- Wait for DNS propagation (typically 5 to 30 minutes, up to 48 hours)
- 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 |
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
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
{
"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 |