Skip to content

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-sts

Path Parameters

ParameterTypeDescription
iduuidDomain ID

Response

json
{
  "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

FieldDescription
enabledWhether MTA-STS is switched on for this domain
modetesting reports failures without blocking delivery; enforce requires TLS
maxAgeHow long receivers cache the policy, in seconds (86400 to 31557600)
mxPatternsThe MX hosts the policy authorises
hostedWhether MailShield serves the policy file
policyIdThe current policy ID. This is what belongs in the _mta-sts TXT record
policyContentThe policy file body that would be served
expectedRecordsThe DNS records this configuration requires
observedWhat 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:

FieldDescription
policyFetchedtrue 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
dnsIdThe policy ID currently published in your _mta-sts TXT record
policyIdInSyncfalse when dnsId differs from policyId — see below
valid / errorsWhether 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

bash
curl -H "Authorization: Bearer ms_your_token" \
  https://app.mailshield.app/api/v1/domains/550e8400-e29b-41d4-a716-446655440000/mta-sts

Update MTA-STS Configuration

PATCH /api/v1/domains/{id}/mta-sts

Request Body

FieldTypeDescription
enabledbooleanSwitch MTA-STS on or off
modestringtesting or enforce
maxAgeintegerPolicy lifetime in seconds (86400 to 31557600)
mxPatternsstring[]MX hosts the policy authorises
hostedbooleanWhether 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

bash
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-sts

Errors

  • 400 - Invalid body, or enforce requested with an empty mxPatterns
  • 403 - Hosted MTA-STS is a paid feature and your plan does not include it
  • 404 - 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:

  1. PATCH returns the new policyId and an updated expectedRecords
  2. observed.policyIdInSync reads false until you republish the TXT record
  3. Republish _mta-sts.<domain> with the new v=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:

bash
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

Monitor and secure your email domains.