Quick answer

Request https://dns.lifestep.io/domain?name=example.com to receive DNS information and a verified port-443 TLS summary as JSON. For lightweight monitoring, inspect has_spf, spf_record, has_dmarc, dmarc_record, and tls.days_until_expiry.

Presence is only the first question. A published SPF record may still be invalid, an apparently healthy DMARC record may remain in monitoring mode forever, and a successful TLS handshake is narrower than a full certificate-chain and protocol audit.

What SPF and DMARC actually do

Sender Policy Framework (SPF) lets a domain publish which sending hosts may use that domain in the SMTP envelope MAIL FROM or HELO identity. The receiving mail server evaluates the connecting IP against the policy. SPF does not authenticate the visible From header, encrypt mail, inspect message content, or prove that an authorized sender is trustworthy.

Domain-based Message Authentication, Reporting, and Conformance (DMARC) applies to the domain in the visible From header. It passes when at least one supported authentication path passes and aligns: SPF with an aligned envelope domain, or DKIM with an aligned signing domain. DMARC also publishes receiver policy and reporting destinations. That alignment is why DMARC addresses a gap that SPF alone leaves.

SPF mistakes that turn a record into false confidence

A domain must not publish multiple TXT records that each begin with v=spf1. They are not combined; multiple SPF policies produce a permanent error. Merge authorized senders into one record instead. Long records may be split into multiple quoted strings inside one TXT resource record, which is different from publishing multiple SPF records.

SPF evaluation also limits DNS-triggering terms to ten. The budget includes mechanisms such as include, a, mx, exists, and redirects, including work reached through includes. Exceeding the limit results in a permanent error. Count the expanded dependency tree, not merely the number of words in the top-level record.

Finally, ~all is softfail: the host probably is not authorized, but the receiver applies its own handling. -all is fail: the domain explicitly says unmatched hosts are not authorized. Moving to -all before inventorying every legitimate sender can reject valid mail, but leaving ~all indefinitely weakens the policy signal. Test real sending paths and change deliberately.

How to read a DMARC record

v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; adkim=s; aspf=r
Common DMARC tags and their operational meaning
Tag Meaning What to check
v=DMARC1 Protocol version It must begin the DMARC record
p Policy for failing organizational-domain mail none, quarantine, or reject
rua Aggregate report destination Mailbox ownership, authorization, and report processing
pct Legacy sampling tag removed by RFC 9989 Do not rely on it for a current rollout
adkim, aspf Strict or relaxed identifier alignment Whether subdomains may align organizationally
sp Policy for subdomains Whether it intentionally differs from p

p=none asks receivers to monitor rather than quarantine or reject because of DMARC. That is sensible during discovery, especially while reviewing aggregate reports for forgotten senders. The common failure is treating discovery as completion: a record stays at p=none for years, reports go unread, and no enforcement decision is made. Promotion is not automatic; confirm aligned legitimate traffic, fix exceptions, then choose a policy appropriate to the domain's risk.

Use the API fields without over-reading them

curl --fail --silent --show-error \
  'https://dns.lifestep.io/domain?name=example.com' |
  jq '{
    has_spf,
    spf_record,
    has_dmarc,
    dmarc_record,
    tls: {days_until_expiry: .tls.days_until_expiry}
  }'

For example.com, the live response exposes the real policy strings in spf_record and dmarc_record, while the TLS day count changes as the active certificate ages or is renewed. Read the response at run time instead of copying a dated number into documentation.

A true has_spf or has_dmarc means a matching record was detected. This endpoint does not fully parse the policy, enumerate the expanded SPF lookup graph, identify multiple SPF policies, validate every DMARC tag, process DMARC reports, or check DKIM selectors. Use dedicated validators and mail-flow evidence before changing enforcement.

Why TLS certificate expiry monitoring matters

An expired HTTPS certificate can turn a healthy application into browser warnings, failed API clients, broken webhooks, and unavailable automation. Renewal systems reduce the risk, but they can fail because of DNS changes, challenge routing, permissions, rate limits, load-balancer drift, or a certificate installed on only some endpoints. Monitoring the certificate actually presented on port 443 tests a different layer from checking a renewal job's status.

The API resolves public A and AAAA records and performs a normal, certificate-verifying TLS handshake to port 443 with the requested domain as the server name. On success, tls.days_until_expiry is an integer derived from the presented certificate's not_after time. Alert early enough to investigate; common operational thresholds are a warning at 30 days and a failure at 14 or 7 days, adjusted to renewal cadence and ownership.

A successful handshake is not a full chain audit. It shows that this client could build a trusted chain and verify the hostname at that moment. A deeper audit may inventory every served intermediate across endpoints, test protocol versions and cipher suites, inspect key sizes and signature algorithms, check revocation or Certificate Transparency expectations, validate OCSP stapling, assess DANE or HSTS, and compare CDN or load-balancer nodes. The API also does not follow application redirects or prove that every network path serves the same certificate.

Automate SPF, DMARC, and TLS expiry in CI or cron

Use presence and expiry as fast regression gates, then schedule deeper policy validation separately. This shell check fails if SPF or DMARC disappears, if TLS data is unavailable, or if the certificate has fewer than 14 days remaining:

#!/usr/bin/env bash
set -euo pipefail

domain="${1:?usage: check-domain DOMAIN}"
json="$(curl --fail --silent --show-error --get \
  'https://dns.lifestep.io/domain' \
  --data-urlencode "name=$domain")"

jq -e '
  .has_spf == true and
  .has_dmarc == true and
  (.tls.days_until_expiry | type == "number") and
  .tls.days_until_expiry >= 14
' <<<"$json" >/dev/null

jq '{domain, has_spf, spf_record, has_dmarc,
     dmarc_record, tls_days: .tls.days_until_expiry}' \
  <<<"$json"

In CI, run the script for domains whose DNS policy is managed by the repository. Avoid blocking unrelated application builds on a transient DNS or network failure unless that tradeoff is intentional. A scheduled workflow is usually better for operational monitoring because it runs even when nobody commits code.

Classify failures before alerting. A successful response with has_dmarc=false is a policy regression; an HTTP error, timeout, or null field is an observation failure that may need a retry. After an intentional DNS edit, recursive resolvers can retain the previous answer until its TTL expires. Record the requested domain, timestamp, full response, and resolver vantage point so an operator can distinguish propagation from an accidental deletion. Never print private deployment secrets alongside the diagnostic.

Design a CI check around declared expectations

Presence alone is too weak for a domain you control. Keep an expected policy file in the repository: whether the domain is supposed to send mail, the approved SPF ending, the intended DMARC policy, the minimum TLS days, and the team that owns each alert. Compare normalized live values with that declaration. A parked domain might intentionally publish v=spf1 -all and p=reject, while a complex mail domain may be in a documented migration. The same generic assertion is not correct for both.

Run monitoring on a schedule and also after reviewed DNS or certificate-infrastructure changes. Give network failures a small, bounded retry with backoff; do not retry a valid response whose policy contradicts the declaration. Pin the script and review changes like production code. If the public API is an external dependency for a critical gate, define how outages are handled and consider running the open-source service in a controlled environment. Either way, independently validate high-impact policy changes before authorizing mail rejection.

# Run every morning at 06:15; use an absolute script path.
15 6 * * * /opt/domain-monitor/check-domain example.com \
  >>/var/log/domain-monitor.log 2>&1

Cron output alone is not an alert. Connect failures to a route someone owns, such as an incident system or monitored email, and suppress repeated notifications without hiding recovery. Store the previous policy strings so an unexpected change from -all to ~all, or from p=reject to p=none, is visible even when both records still exist. Recheck from another resolver or network before treating one observation as authoritative.

Limits and common questions

Does this API return domain registration expiry?

No. It provides DNS and TLS information only. It does not query registrar WHOIS or RDAP data, so TLS certificate expiry must not be confused with domain-registration expiry, ownership, creation date, registrar status, or contact information.

Does a clean response mean the domain is secure?

No. This is not a security scanner and does not certify a website, mail system, sender, or domain as safe. DNS answers vary by resolver, geography, caching, time, and split-horizon configuration. TLS is reported only when a verified port-443 handshake succeeds; a null TLS object can reflect timeout, resolution, reachability, or certificate-verification failure.

What should be checked beyond these three signals?

Validate the complete SPF dependency graph, DMARC syntax and reporting authorization, real SPF/DKIM alignment from message headers, DKIM key rotation, and aggregate DMARC reports. For HTTPS, test all relevant endpoints and perform a deliberate protocol and certificate-chain audit. These checks complement the compact API rather than being replaced by it.

Standards references