MTA-STS
Read and change a domain's MTA-STS configuration, including the DNS records it requires and what was last observed in DNS.
MTA-STS tells sending servers to require TLS when delivering to your domain. It needs two DNS records and a policy file served over HTTPS. MailShield can host the policy file for you.
Get MTA-STS Configuration
GET /api/v1/domains/{id}/mta-stsPath Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Domain ID |
Response
{
"data": {
"enabled": true,
"mode": "testing",
"maxAge": 604800,
"mxPatterns": ["mail.example.com", "*.example.com"],
"hosted": true,
"policyId": "ha7rqCmk",
"policyContent": "version: STSv1\nmode: testing\nmx: mail.example.com\nmx: *.example.com\nmax_age: 604800\n",
"policyUrl": "https://mta-sts.example.com/.well-known/mta-sts.txt",
"updatedAt": "2024-01-15T10:30:00Z",
"expectedRecords": [
{
"purpose": "mta-sts-policy-host",
"name": "mta-sts.example.com",
"type": "CNAME",
"value": "mta-sts.mailshield.app."
},
{
"purpose": "mta-sts-policy-id",
"name": "_mta-sts.example.com",
"type": "TXT",
"value": "v=STSv1; id=ha7rqCmk"
}
],
"observed": {
"dnsId": "ha7rqCmk",
"policyFetched": true,
"policyMode": "testing",
"valid": true,
"errors": [],
"checkedAt": "2024-01-15T10:30:00Z",
"policyIdInSync": true
}
}
}Fields
| Field | Description |
|---|---|
enabled | Whether MTA-STS is switched on for this domain |
mode | testing reports failures without blocking delivery; enforce requires TLS |
maxAge | How long receivers cache the policy, in seconds (86400 to 31557600) |
mxPatterns | The MX hosts the policy authorises |
hosted | Whether MailShield serves the policy file |
policyId | The current policy ID. This is what belongs in the _mta-sts TXT record |
policyContent | The policy file body that would be served |
expectedRecords | The DNS records this configuration requires |
observed | What the most recent check saw. null until the domain has been checked once |
The observed block
observed is the answer to "is this actually live yet". It reflects the last check rather than your configuration:
| Field | Description |
|---|---|
policyFetched | true when the policy file was retrieved over HTTPS. This also confirms the policy host completed a TLS handshake for your domain, so it is the signal to wait on after publishing the CNAME |
dnsId | The policy ID currently published in your _mta-sts TXT record |
policyIdInSync | false when dnsId differs from policyId — see below |
valid / errors | Whether the observed setup validates, and why not |
Waiting for the policy host
After you publish the CNAME, the policy host needs to issue a certificate for mta-sts.<your-domain> before it can serve anything. Poll this endpoint and wait for observed.policyFetched to become true rather than guessing at a delay. Publishing DNS before enabling the domain will not work: the policy host does not issue certificates for domains that are switched off.
Example
curl -H "Authorization: Bearer ms_your_token" \
https://app.mailshield.app/api/v1/domains/550e8400-e29b-41d4-a716-446655440000/mta-stsUpdate MTA-STS Configuration
PATCH /api/v1/domains/{id}/mta-stsRequest Body
| Field | Type | Description |
|---|---|---|
enabled | boolean | Switch MTA-STS on or off |
mode | string | testing or enforce |
maxAge | integer | Policy lifetime in seconds (86400 to 31557600) |
mxPatterns | string[] | MX hosts the policy authorises |
hosted | boolean | Whether MailShield serves the policy file |
All fields are optional; omitted fields keep their current value. Unknown fields are rejected.
Response
Returns the same shape as GET, reflecting the updated configuration.
Example
curl -X PATCH \
-H "Authorization: Bearer ms_your_token" \
-H "Content-Type: application/json" \
-d '{"enabled": true, "mode": "testing", "mxPatterns": ["mail.example.com"]}' \
https://app.mailshield.app/api/v1/domains/550e8400-e29b-41d4-a716-446655440000/mta-stsErrors
400- Invalid body, orenforcerequested with an emptymxPatterns403- Hosted MTA-STS is a paid feature and your plan does not include it404- Domain not found
Start in testing mode
New domains default to mode: testing. Serving enforce to a domain whose MX set has not been confirmed causes hard delivery failures on live mail, so switch to enforce only once observed.policyFetched is true and the reports look clean.
Enabling enforce with an empty mxPatterns is rejected with a 400, because that would serve a policy matching no host and break delivery for every strict-TLS sender.
Policy ID rotation
The id= in your _mta-sts TXT record is what tells receivers to drop a cached policy. If it does not change, receivers keep honouring the old policy until max_age expires — potentially a week.
MailShield rotates policyId automatically whenever the served policy changes, meaning any change to mode, maxAge or mxPatterns. When that happens:
PATCHreturns the newpolicyIdand an updatedexpectedRecordsobserved.policyIdInSyncreadsfalseuntil you republish the TXT record- Republish
_mta-sts.<domain>with the newv=STSv1; id=<policyId>
Poll GET and treat observed.policyIdInSync === false as "DNS needs republishing". This is the drift that is otherwise invisible: the policy you serve and the policy receivers cache silently diverge.
Scripted setup
A full enable-and-publish loop:
DOMAIN_ID="550e8400-e29b-41d4-a716-446655440000"
AUTH="Authorization: Bearer ms_your_token"
# 1. Enable in testing mode with your MX hosts
curl -sX PATCH -H "$AUTH" -H "Content-Type: application/json" \
-d '{"enabled": true, "mode": "testing", "mxPatterns": ["mail.example.com"], "hosted": true}' \
"https://app.mailshield.app/api/v1/domains/$DOMAIN_ID/mta-sts"
# 2. Read the records to publish
curl -s -H "$AUTH" \
"https://app.mailshield.app/api/v1/domains/$DOMAIN_ID/mta-sts" \
| jq -r '.data.expectedRecords[] | "\(.name) \(.type) \(.value)"'
# 3. Publish those records in your DNS, then poll until the policy is live
until curl -s -H "$AUTH" \
"https://app.mailshield.app/api/v1/domains/$DOMAIN_ID/mta-sts" \
| jq -e '.data.observed.policyFetched == true' >/dev/null; do sleep 30; done