TLS-RPT JSON Report Format: policies, summary, failure-details, result-type Catalogue, MIME Types, and Redelivery Semantics
Field-by-field reference for the TLS-RPT aggregate JSON report (RFC 8460 §4): every object and field in the on-the-wire schema with type, semantics, and operator action, the 12 canonical result-type values with per-value remediation guidance, the application/tlsrpt+json vs application/tlsrpt+gzip MIME distinction, the mailto: attachment filename convention, the report-id-keyed redelivery and deduplication semantics, five worked example reports, and ten common ingestion-pipeline failure modes.
This reference is provided in English. TLS-RPT is specified in an English-language RFC (RFC 8460) and the canonical schema vocabulary (JSON field names, result-type values, application/tlsrpt+json) does not translate. We keep the body in the source language so technical terms remain consistent with the specification.
TL;DR
A TLS-RPT report is a JSON document (MIME
application/tlsrpt+json or
application/tlsrpt+gzip when
compressed) whose top-level object carries
metadata plus a
policies[] array. The
on-the-wire schema is defined by
RFC 8460 §4.
- Top-level fields:
organization-name(free-form),date-range(ISO 8601 UTC start/end),contact-info,report-id(dedup key),policies[]. - Per-policy fields:
policy.policy-typeis one ofsts,tlsa, orno-policy-found;policy.policy-stringreproduces the evaluated policy verbatim;policy.policy-domainandpolicy.mx-hostidentify the scope. - Summary:
summary.total-successful-session-countandsummary.total-failure-session-count— both counts are per SESSION, not per message. - Failure details:
failure-details[]array of per-failure-shape records.result-typeclassifies the failure into one of 12 canonical values;failed-session-countaggregates sessions with the same (result-type,sending-mta-ip,receiving-mx-hostname) shape. - 12 canonical
result-typevalues:starttls-not-supported,certificate-host-mismatch,certificate-expired,certificate-not-trusted,validation-failure,sts-policy-fetch-error,sts-policy-invalid,sts-webpki-invalid,tlsa-invalid,dnssec-invalid,dane-required,certificate-error. - Filename convention (mailto:
delivery):
<reporter>!<policy-domain>!<unix-start>!<unix-end>.json[.gz]. Exclamation-mark separator. Tracks the 24-hour aggregation window. - Dedup key: the top-level
report-id. Receivers MUST skip re-ingestion of any report whosereport-idhas already been processed.
DMARC Analyzer ingests TLS-RPT reports and surfaces per-policy session counts and per-result-type breakdowns in the dashboard. The Domain Security Checker confirms the TLS-RPT DNS record that triggers report delivery.
1. What "JSON report format" means for TLS-RPT (and where the syntax lives)
TLS-RPT is a layered specification. The
SYNTAX LAYER defines how the
recipient domain advertises a reporting
destination at the DNS — the
v=TLSRPTv1; rua=<uri-list>
record at _smtp._tls.<domain>
plus the cross-domain authorisation record at
<recipient>._report._smtp._tls.<external>.
The SEMANTIC LAYER defines what
the actual report document looks like once
delivered: the JSON schema, field semantics,
result-type catalogue, MIME types, filename
conventions, and redelivery rules.
This page covers the SEMANTIC LAYER exhaustively. The syntax layer is covered by the sibling reference pages:
- TLS-RPT DNS record syntax
— the focused byte-level reference for the
_smtp._tls.<domain>TXT record per RFC 8460 §3 (v=TLSRPTv1,rua=, schemes, cross-domain authorisation, 255-octet chunking). - TLS-RPT explained — the protocol pillar — the whole-protocol survey (what TLS-RPT is, how it works, deployment workflow, relationship to MTA-STS and DANE).
Why the schema layer matters
independently: the report-format
semantics determine what an ingestion pipeline
can DO with the data. A parser that does not
understand policy-type=no-policy-found
loses the visibility-into-opportunistic-TLS
signal entirely. A parser that conflates
failed-session-count with
message-count produces misleading dashboards. A
parser that does not dedup on
report-id double-counts redelivered
reports. The schema-layer failure modes are
operator-introduced and trivially avoidable once
the schema is understood. Most "my TLS-RPT
dashboard numbers do not match my MTA logs"
tickets trace back to a schema-layer
misunderstanding.
The canonical TLS-RPT report document:
{
"organization-name": "Example Reporter Inc.",
"date-range": {
"start-datetime": "2026-05-17T00:00:00Z",
"end-datetime": "2026-05-17T23:59:59Z"
},
"contact-info": "tlsrpt-support@reporter.example",
"report-id": "2026-05-17T00:00:00Z_example.com",
"policies": [
{
"policy": {
"policy-type": "sts",
"policy-string": [
"version: STSv1",
"mode: enforce",
"mx: mail.example.com",
"max_age: 604800"
],
"policy-domain": "example.com",
"mx-host": "mail.example.com"
},
"summary": {
"total-successful-session-count": 12450,
"total-failure-session-count": 37
},
"failure-details": [
{
"result-type": "certificate-host-mismatch",
"sending-mta-ip": "203.0.113.5",
"receiving-mx-hostname": "mail.example.com",
"receiving-mx-helo": "mail.example.com",
"receiving-ip": "203.0.113.10",
"failed-session-count": 37,
"additional-information": "https://reporter.example/tlsrpt/incidents/12345",
"failure-reason-code": "X509: cert SAN does not include MX hostname"
}
]
}
]
}
Every section below documents one object or field in this schema.
2. Top-level fields (RFC 8460 §4.1)
The top-level JSON object carries five fields
plus the policies[] array.
organization-name-
Type: string (free-form,
UTF-8).
Required: yes.
Semantics: the human-readable name of the reporting organisation. Used for dashboard display and reporter attribution. Examples: "Google Inc.", "Microsoft Corporation", "Yahoo!". Not interpreted programmatically by receivers — used for display only. date-range-
Type: object with two
ISO-8601 string fields.
Required: yes.
Semantics: the time window the aggregated counts cover.start-datetimeis the inclusive start of the window;end-datetimeis the inclusive end. RFC 8460 §4.1 specifies ISO 8601 UTC with the Z suffix (2026-05-17T00:00:00Z). The canonical window length is 24 hours. contact-info-
Type: string (free-form).
Required: yes (may be empty).
Semantics: follow-up contact channel for the reporter — usually an email address or operator-portal URL where the recipient operator can ask questions about the report. Not parsed programmatically. report-id-
Type: string (reporter-
defined opaque identifier).
Required: yes.
Semantics: reporter- assigned unique identifier for the report document. Receivers MUST use this as the dedup key when ingesting redelivered reports. Format is reporter-defined; Google uses ISO 8601 timestamps concatenated with the policy domain, Microsoft uses GUIDs. Treat as opaque at ingestion. policies[]-
Type: array of policy
objects (see §3).
Required: yes (may be empty array).
Semantics: one entry per distinct recipient policy seen during the aggregation window. The vast majority of reports contain exactly one entry; reports covering multiple recipient domains or mixed sts+tlsa+no-policy-found observations contain multiple entries.
3. The policy
object (RFC 8460 §4.2)
Each entry in policies[] contains a
policy object identifying which
policy the reporter evaluated.
policy.policy-type-
Type: enum-string, one of
sts/tlsa/no-policy-found.
Required: yes.
Semantics:stsmeans the sender evaluated an MTA-STS policy (RFC 8461).tlsameans the sender evaluated a DANE TLSA record (RFC 7672).no-policy-foundmeans neither applied — the session used opportunistic TLS or plaintext. Each value implies a different shape for thepolicy-stringfield. policy.policy-string-
Type: array of strings.
Required: yes (may be empty forno-policy-found).
Semantics: the verbatim policy that was evaluated, reproduced line-by-line. Forpolicy-type=ststhis is the per-line content of the retrievedmta-sts.txtfile (e.g.,["version: STSv1", "mode: enforce", "mx: mail.example.com", "max_age: 604800"]). Forpolicy-type=tlsathis is the TLSA resource-record values formatted as strings. Forpolicy-type=no-policy-foundthis is typically an empty array ([]). policy.policy-domain-
Type: string (DNS
name).
Required: yes.
Semantics: the recipient domain whose policy was evaluated. Typically the same as theTo:-header recipient domain but may differ when the sender follows CNAMEs or when multiple policy domains share an MX. policy.mx-host-
Type: string (DNS name or
wildcard pattern).
Required: no — present when the policy named a specific MX.
Semantics: the MX hostname (or wildcard pattern from the MTA-STS policy) involved in this aggregation bucket. Forpolicy-type=ststhis is one of themx:entries in the MTA-STS policy file.
4. The summary
object (RFC 8460 §4.2)
Each policy entry includes a single
summary object with two integer
counts.
summary.total-successful-session-count-
Type: non-negative
integer.
Required: yes.
Semantics: count of inbound SMTP sessions that succeeded under the policy during the window. "Success" means the sender's TLS validation passed and the session proceeded (i.e., the policy did not cause the connection to abort). Per SESSION, not per MESSAGE — a single TCP connection may carry multiple messages. summary.total-failure-session-count-
Type: non-negative
integer.
Required: yes.
Semantics: count of inbound SMTP sessions that failed the policy. This is the sum of allfailure-details[].failed-session-countentries in the same policy. Verifying that the sum matches thetotal-failure-session-countis a good sanity check for ingestion pipelines.
5. The
failure-details[] array
(RFC 8460 §4.3)
Each policy entry includes an array of
per-failure-shape records. Each entry describes
a distinct failure mode aggregated over the
window, deduplicated by the
(result-type,
sending-mta-ip,
receiving-mx-hostname) tuple.
result-type-
Type: enum-string, one of
12 canonical values (see §6).
Required: yes.
Semantics: classifies the failure into one of the canonical categories defined in RFC 8460 §4.3. Determines the operator remediation step. sending-mta-ip-
Type: string (IPv4 or IPv6 address).
Required: yes.
Semantics: the IP address (IPv4 or IPv6) of the sending MTA that attempted the TLS session. Used to attribute the failure to a specific outbound IP. To identify the operator, do a reverse-DNS / WHOIS lookup on the IP. Not the HELO/EHLO hostname, the From: header domain, or the MAIL FROM envelope. receiving-mx-hostname-
Type: string (DNS name).
Required: yes.
Semantics: the MX hostname the sender attempted to deliver to. One of the values in the recipient domain's MX RRset. receiving-mx-helo-
Type: string (DNS name).
Required: no.
Semantics: the EHLO/HELO identity the receiving MX advertised when the connection was established. Usually equal toreceiving-mx-hostnamebut may differ when the MX is behind a load-balancer with a separate HELO name. receiving-ip-
Type: string (IPv4 or
IPv6 address).
Required: no.
Semantics: the IP address of the specific MX instance hit. Useful when the operator runs multiple backends behind a single hostname. Reporters MAY omit it for policy-fetch-layer failures where no TCP connection occurred. failed-session-count-
Type: non-negative
integer.
Required: yes.
Semantics: count of sessions matching this failure shape during the window. Per SESSION, not per MESSAGE. additional-information-
Type: string (URL or
free-form).
Required: no.
Semantics: reporter- specific incident pointer — typically a URL to an incident page in the reporter's operations portal. Stored verbatim by receivers; not parsed. failure-reason-code-
Type: string
(reporter-defined identifier).
Required: no.
Semantics: a more machine-readable identifier complementingresult-type. Reporter- defined; common values include OpenSSL or BoringSSL error names ("X509: cert SAN does not include MX hostname", "X509: certificate has expired", "DANE: TLSA certificate mismatch").
6. The 12 canonical
result-type values
(RFC 8460 §4.3)
RFC 8460 §4.3 defines a small catalogue of
result-type values that classify
TLS failures. Each value maps to a specific
deployment defect the recipient operator can
act on.
| result-type | What it means | Typical remediation |
|---|---|---|
starttls-not-supported |
The receiving MX did not advertise STARTTLS in its EHLO response. | Enable STARTTLS on every MX. Modern
Postfix / Exim / Microsoft Exchange
support it by default; check the
smtpd_tls_security_level
(Postfix) or equivalent
configuration. |
certificate-host-mismatch |
The leaf certificate's SAN does not cover the MX hostname. | Reissue the certificate with the MX
hostname in the SAN list. Most-
common cause: certificate covers
only the bare domain
(example.com) instead
of the MX
(mail.example.com). |
certificate-expired |
The leaf or intermediate
certificate is past its
notAfter date. |
Renew the certificate. Automate
renewal with certbot,
acme.sh, or
infrastructure-as-code; calendar
a replacement at
notAfter - 30 days. |
certificate-not-trusted |
The certificate chain does not link to a trusted root in the sender's CA store. | Serve the full intermediate chain
(not just the leaf). Check with
openssl s_client -showcerts
-starttls smtp -connect <mx>:25. |
validation-failure |
A generic certificate validation error that does not fit a more specific category. | Inspect
additional-information
or failure-reason-code
fields for the reporter's
specifics. |
sts-policy-fetch-error |
The sender could not retrieve the
MTA-STS policy file from
https://mta-sts.<domain>/.well-known/mta-sts.txt. |
Check that the HTTPS host serves
the policy file with a valid
public-PKI certificate, a 200
response, and
Content-Type: text/plain.
See
MTA-STS troubleshooting. |
sts-policy-invalid |
The retrieved MTA-STS policy file did not parse as a valid policy. | Re-check the policy file syntax:
version: STSv1 on the
first line, valid mode,
mx: entries, and a
positive integer
max_age. No BOM byte
at file head. |
sts-webpki-invalid |
The HTTPS host serving the MTA-STS policy file presented a certificate that failed public-PKI validation. | Replace the certificate on the
mta-sts.<domain>
vhost. Self-signed and private-PKI
certificates are rejected. |
tlsa-invalid |
The TLSA record for the MX evaluated against the presented certificate but did not match. | Reissue the TLSA record from the current certificate or public key hash. Common cause: certificate rotation without updating TLSA. |
dnssec-invalid |
DNSSEC validation failed for the recipient's DNS zone (which DANE requires). | Audit the zone's DNSSEC chain (DS record at the parent, signed RRSIGs in the zone). DNSSEC misconfigurations fail closed for DANE. |
dane-required |
DANE is required by sender policy but the recipient does not publish a TLSA record. | Either deploy DANE (publish DNSSEC-signed TLSA records under each MX) or confirm the sender treats DANE as opportunistic for your domain. |
certificate-error |
Generic certificate error category used by older reporters when the specific failure does not match a better-known result-type. | Inspect
additional-information;
otherwise treat as
certificate-not-trusted /
certificate-host-mismatch and
start there. |
7. MIME types
(application/tlsrpt+json and
application/tlsrpt+gzip)
RFC 8460 §4 defines two MIME types for TLS-RPT report delivery:
application/tlsrpt+json- Uncompressed UTF-8 JSON document. Used for small reports (typically under 50KB) and as the default for HTTPS POST delivery. Receiver parses the body directly as JSON.
application/tlsrpt+gzip-
Gzip-compressed JSON document. The report
body is a single gzip stream that
decompresses to the
application/tlsrpt+jsonpayload. Reporters typically use this when the uncompressed JSON exceeds approximately 50KB; gzip cuts the size by 80% or more for repetitive aggregated data. Receiver MUST gunzip before parsing.
For HTTPS delivery, the MIME
type is conveyed in the
Content-Type request header. For
mailto: delivery, the MIME
type is conveyed in the
Content-Type of the email
attachment and the filename suffix
(.json vs
.json.gz) provides a redundant
signal.
Some receivers also accept the older
application/x-gzip or
application/gzip MIME types as
fallbacks for legacy reporters that have not
migrated to application/tlsrpt+gzip.
Robust ingestion pipelines inspect the body's
first two bytes — the gzip magic number
1F 8B — to decide whether to
decompress, ignoring the declared MIME type
when it conflicts with the actual body shape.
8. The mailto: attachment filename convention
For mailto: delivery, RFC 8460
§5.1 specifies the attachment filename as:
<reporter>!<policy-domain>!<unix-start>!<unix-end>.json[.gz]
Four exclamation-mark-separated components:
<reporter>- The reporting organisation's domain
(e.g.,
google.com,outlook.com). <policy-domain>- The recipient domain whose policy the
report covers (e.g.,
example.com). <unix-start>- Unix epoch seconds at the start of the 24-hour aggregation window.
<unix-end>- Unix epoch seconds at the end of the 24-hour aggregation window.
Example:
google.com!example.com!1747353600!1747439999.json.gz
Meaning: Google reporting on
example.com for the 24-hour
window starting 2026-05-15T00:00:00Z, gzip-
compressed. The exclamation-mark separator
lets receivers sort filenames lexically by
reporter then domain then time without
fancy parsing. The
.json suffix indicates
uncompressed; .json.gz
indicates gzip.
Receivers SHOULD use the filename for log
routing and operational tracking, and use
the in-body report-id for
semantic deduplication.
9. Redelivery and deduplication
(report-id semantics)
Reporters MAY redeliver reports if the initial
delivery failed (5xx HTTP response, mailbox
bounce, network timeout) or if internal
reporter operations re-run the aggregation
pipeline. Redelivered reports carry the SAME
report-id as the original;
receivers MUST use report-id as
the deduplication key.
Operational rule: ingestion
pipelines maintain a set of seen
report-id values (typically
indexed in a database with a unique constraint
on the column). On every incoming report, the
pipeline checks the set; if the
report-id is already present, the
report is logged as a duplicate and the
parse-and-store step is skipped.
Dedup window: recipients
typically retain seen
report-id values for the report
retention window (often 30-90 days). Older
reports are unlikely to be redelivered after
that horizon.
Cross-reporter collisions:
different reporters MAY use overlapping
report-id formats (e.g., two
reporters both using ISO 8601 timestamps).
Robust receivers dedup on the tuple
(organization-name,
report-id) rather than
report-id alone to avoid the
rare cross-reporter collision case. RFC 8460
does not mandate this but it is a defensive
pattern.
Backfill semantics: some
reporters provide a "backfill" mechanism
where the operator can request historical
reports for a date range. The backfill API
typically reuses the original
report-id values from the
original aggregation window — so a
well-implemented receiver dedupes the
backfilled reports against the originally-
received ones without operator
intervention.
10. Canonical examples
Example 1 — minimal report, no-policy-found, all successes:
{
"organization-name": "Example Reporter Inc.",
"date-range": {
"start-datetime": "2026-05-17T00:00:00Z",
"end-datetime": "2026-05-17T23:59:59Z"
},
"contact-info": "tlsrpt-support@reporter.example",
"report-id": "2026-05-17T00:00:00Z_example.com_001",
"policies": [
{
"policy": {
"policy-type": "no-policy-found",
"policy-string": [],
"policy-domain": "example.com"
},
"summary": {
"total-successful-session-count": 5000,
"total-failure-session-count": 0
},
"failure-details": []
}
]
}
Example 2 — MTA-STS report with a certificate-host-mismatch failure:
{
"organization-name": "Example Reporter Inc.",
"date-range": {
"start-datetime": "2026-05-17T00:00:00Z",
"end-datetime": "2026-05-17T23:59:59Z"
},
"contact-info": "tlsrpt-support@reporter.example",
"report-id": "2026-05-17T00:00:00Z_example.com_002",
"policies": [
{
"policy": {
"policy-type": "sts",
"policy-string": [
"version: STSv1",
"mode: enforce",
"mx: mail.example.com",
"max_age: 604800"
],
"policy-domain": "example.com",
"mx-host": "mail.example.com"
},
"summary": {
"total-successful-session-count": 12450,
"total-failure-session-count": 37
},
"failure-details": [
{
"result-type": "certificate-host-mismatch",
"sending-mta-ip": "203.0.113.5",
"receiving-mx-hostname": "mail.example.com",
"receiving-mx-helo": "mail.example.com",
"receiving-ip": "203.0.113.10",
"failed-session-count": 37,
"additional-information": "https://reporter.example/tlsrpt/incidents/12345",
"failure-reason-code": "X509: cert SAN does not include MX hostname"
}
]
}
]
}
Example 3 — multi-failure report (sts-policy-fetch-error + starttls-not-supported):
{
"organization-name": "Example Reporter Inc.",
"date-range": {
"start-datetime": "2026-05-17T00:00:00Z",
"end-datetime": "2026-05-17T23:59:59Z"
},
"contact-info": "tlsrpt-support@reporter.example",
"report-id": "2026-05-17T00:00:00Z_example.com_003",
"policies": [
{
"policy": {
"policy-type": "sts",
"policy-string": [
"version: STSv1",
"mode: enforce",
"mx: mail.example.com",
"max_age: 604800"
],
"policy-domain": "example.com",
"mx-host": "mail.example.com"
},
"summary": {
"total-successful-session-count": 12000,
"total-failure-session-count": 105
},
"failure-details": [
{
"result-type": "sts-policy-fetch-error",
"sending-mta-ip": "203.0.113.10",
"receiving-mx-hostname": "mail.example.com",
"failed-session-count": 100,
"failure-reason-code": "HTTP 503 from mta-sts.example.com"
},
{
"result-type": "starttls-not-supported",
"sending-mta-ip": "203.0.113.20",
"receiving-mx-hostname": "backup.example.com",
"failed-session-count": 5,
"failure-reason-code": "EHLO response missing STARTTLS"
}
]
}
]
}
Example 4 — DANE TLSA report:
{
"organization-name": "Example Reporter Inc.",
"date-range": {
"start-datetime": "2026-05-17T00:00:00Z",
"end-datetime": "2026-05-17T23:59:59Z"
},
"contact-info": "tlsrpt-support@reporter.example",
"report-id": "2026-05-17T00:00:00Z_secure.example_001",
"policies": [
{
"policy": {
"policy-type": "tlsa",
"policy-string": [
"3 1 1 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd"
],
"policy-domain": "secure.example",
"mx-host": "mail.secure.example"
},
"summary": {
"total-successful-session-count": 800,
"total-failure-session-count": 2
},
"failure-details": [
{
"result-type": "tlsa-invalid",
"sending-mta-ip": "2001:db8::1",
"receiving-mx-hostname": "mail.secure.example",
"failed-session-count": 2,
"failure-reason-code": "DANE: TLSA hash mismatch after cert rotation"
}
]
}
]
}
Example 5 — multi-policy report (two policy domains in a single document):
{
"organization-name": "Example Reporter Inc.",
"date-range": {
"start-datetime": "2026-05-17T00:00:00Z",
"end-datetime": "2026-05-17T23:59:59Z"
},
"contact-info": "tlsrpt-support@reporter.example",
"report-id": "2026-05-17T00:00:00Z_multi_001",
"policies": [
{
"policy": {
"policy-type": "sts",
"policy-string": ["version: STSv1", "mode: enforce", "mx: mail.example.com", "max_age: 604800"],
"policy-domain": "example.com",
"mx-host": "mail.example.com"
},
"summary": {
"total-successful-session-count": 10000,
"total-failure-session-count": 0
},
"failure-details": []
},
{
"policy": {
"policy-type": "no-policy-found",
"policy-string": [],
"policy-domain": "sub.example.com"
},
"summary": {
"total-successful-session-count": 500,
"total-failure-session-count": 1
},
"failure-details": [
{
"result-type": "starttls-not-supported",
"sending-mta-ip": "203.0.113.5",
"receiving-mx-hostname": "sub.example.com",
"failed-session-count": 1
}
]
}
]
}
11. Common ingestion mistakes
Ten canonical ingestion-pipeline failure modes, enumerated as a punch list. Every one produces incorrect dashboard numbers or silent data loss even when the upstream reports are well-formed.
- Counting
failed-session-countas messages — the field is per SESSION, not per message. A dashboard labelling the count as "failed messages" misleads operators; label as "failed sessions". - Not deduplicating on
report-id— redelivered reports double-count if the ingestion pipeline does not maintain a seen-id set. Use a UNIQUE constraint on thereport_idcolumn. - Ignoring
policy-type=no-policy-found— pipelines that filter forsts+tlsaonly lose the opportunistic-TLS visibility signal. Index every policy-type. - Not gunzipping
application/tlsrpt+gzip— parsing the gzip-compressed body as JSON produces a parse error. Inspect the body's first two bytes (gzip magic1F 8B) before parsing. - Conflating
sending-mta-ipwith From: header domain — they are different identities.sending-mta-ipis the IP address of the connecting sending MTA, not the message-author identity. - Ignoring
failure-details=[]reports — successful aggregation windows produce emptyfailure-detailsarrays. These are NOT errors; they are the baseline "everything was fine" data operators need for trend analysis. - Treating
certificate-erroras cert-trust failure —certificate-erroris the legacy catch-all category. Inspectadditional-information/failure-reason-codefor the specific cause before assigning a remediation. - Hard-failing on unknown
result-typevalues — future RFC updates may add newresult-typevalues. Robust ingestion pipelines log unknown values but store the report anyway under an "other" category. - Not validating that
summary.total-failure-session-countequals the sum offailure-details[].failed-session-count— mismatches indicate either a reporter bug or a truncation during delivery. Worth a log warning. - Storing
policy-stringas a single string — the field is an array of strings (per-line policy content). A pipeline that joins it before storing loses round-tripping fidelity for operator audit.
DMARC Analyzer's ingestion pipeline is built to avoid all ten of these failure modes. See help_tls_rpt_explained.php §7 for the receiver-side architecture.
12. Frequently asked questions
- What is the difference between application/tlsrpt+json and application/tlsrpt+gzip?
- They are the two MIME types RFC 8460 §4 defines for TLS-RPT report delivery. application/tlsrpt+json carries the JSON document verbatim (uncompressed UTF-8). application/tlsrpt+gzip carries the same JSON document gzip-compressed; the report body is a single gzip stream that decompresses to the application/tlsrpt+json payload. Reporters typically use application/tlsrpt+gzip when the uncompressed JSON exceeds approximately 50KB because gzip cuts the size by 80% or more for repetitive aggregated data. The filename suffix follows: .json for uncompressed, .json.gz for gzipped. A receiver MUST inspect the MIME type (for HTTPS delivery) or the filename suffix (for mailto: delivery) to decide whether to gunzip before parsing. Some receivers also accept the older application/x-gzip or application/gzip MIME types as fallbacks but the application/tlsrpt+gzip type is the canonical one.
- Is failed-session-count counting per-message or per-session?
- Per-session, not per-message. RFC 8460 §4.3 specifies failed-session-count as the number of TLS-failed SMTP sessions (TCP connections that attempted STARTTLS or evaluated the TLS policy and failed) within the 24-hour window, deduplicated by the (result-type, sending-mta-ip, receiving-mx-hostname) shape. A single SMTP session may carry zero, one, or many messages depending on connection reuse and pipelining — so failed-session-count is typically smaller than the message-level count. When troubleshooting, the operator interprets failed-session-count as "this many TLS-handshake attempts from this sending MTA hit this failure" rather than "this many emails were affected". For message-level metrics, cross-reference with DMARC RUA reports which count messages directly.
- What does policy-type=no-policy-found mean?
- It means the sending MTA found no MTA-STS policy and no DANE TLSA record for the recipient domain — the session used opportunistic TLS (or plaintext if STARTTLS was not advertised) with no enforced TLS policy to evaluate. RFC 8460 §4.2 specifies no-policy-found as the policy-type for sessions where neither MTA-STS (RFC 8461) nor DANE (RFC 7672) provided a binding policy. The summary still counts successful and failed sessions, but the failure-details entries describe failures of opportunistic TLS rather than policy-enforcement failures. Operationally, no-policy-found entries are useful for assessing the baseline TLS health of the recipient domain BEFORE deploying MTA-STS or DANE — the policy-type=no-policy-found summary shows what fraction of inbound TLS attempts already succeed without enforcement, which informs whether moving to MTA-STS testing mode is safe.
- How do reporters deduplicate redelivered TLS-RPT reports?
- Through the top-level report-id field. RFC 8460 §4.1 specifies report-id as a reporter-assigned unique identifier that recipients use to deduplicate redelivered reports. The recipient ingestion pipeline indexes incoming reports by report-id and skips the parse-and-store step on any report whose report-id has already been ingested. The dedup window is typically the report retention window (often 30-90 days) — long enough to absorb any reasonable redelivery timeline. report-id format is reporter-defined; Google uses ISO 8601 timestamps concatenated with the policy domain (e.g., 2026-05-17T00:00:00Z_example.com), Microsoft uses GUIDs, and other reporters use various concatenations of date + domain + reporter-internal-counter. The recipient ingestion pipeline should not parse report-id semantically — treat it as an opaque dedup key. Cross-reporter collisions are theoretically possible but vanishingly rare in practice given the reporter-internal-counter component.
- What is the filename convention for TLS-RPT email attachments?
- RFC 8460 §5.1 specifies the filename as <reporter>!<policy-domain>!<unix-start>!<unix-end>.json[.gz] with the four exclamation-mark-separated components carrying: <reporter> the reporting organisation domain (e.g., google.com), <policy-domain> the recipient domain whose policy this report covers (e.g., example.com), <unix-start> the Unix epoch seconds of the aggregation window start, <unix-end> the Unix epoch seconds of the aggregation window end. The .json or .json.gz suffix names the encoding (uncompressed JSON vs gzipped). The exclamation-mark separator lets receivers sort filenames lexically by reporter then domain then time. Example: google.com!example.com!1747353600!1747439999.json.gz — meaning Google reporting on example.com for the 24-hour window starting 2026-05-15T00:00:00Z, gzip-compressed. The receiver typically uses the filename for log routing and uses the in-body report-id for dedup.
- What is sending-mta-ip and how do I use it?
- RFC 8460 §4.3 specifies sending-mta-ip as the IPv4 or IPv6 address of the sending MTA that attempted the TLS session that failed. It attributes the failure to a specific outbound IP rather than to a hostname. To identify the responsible mail provider, do a reverse-DNS / WHOIS lookup on that IP, then contact the operator with the failure-details for diagnosis. sending-mta-ip is distinct from the From: header domain (the message-author identity) and the MAIL FROM envelope (the bounce-address identity); it is also NOT the SMTP HELO/EHLO hostname the sender announced — it is the actual connecting IP address observed by the receiving MTA.
- What is the additional-information field and what do reporters put there?
- RFC 8460 §4.3 specifies additional-information as a free-form string the reporter may use to point at reporter-specific incident details — typically a URL to an incident page in the reporter's operations portal where the recipient operator can find packet captures, certificate snapshots, or extended failure traces. The field is optional and reporter-defined; Google fills it with a console URL, Microsoft with an Azure incident link, and smaller reporters typically leave it empty or fill with a short free-text note. Recipient ingestion pipelines store the value verbatim but do not parse it. When troubleshooting a specific failure-details entry, the operator clicks the additional-information URL (if present) to access the reporter-specific incident detail beyond the structured fields. The field complements failure-reason-code (a more machine-readable identifier).
- Why do some failure-details entries lack receiving-ip while others have it populated?
- RFC 8460 §4.3 specifies receiving-ip as the IPv4 or IPv6 address of the receiving MX that the sender connected to (or attempted to connect to). The field is OPTIONAL — reporters MAY include it but are not required to. Reporters that omit it typically do so for one of three reasons: (1) the failure occurred at the policy-evaluation layer before the actual TCP connection (e.g., sts-policy-fetch-error happens when fetching the policy file, not when connecting to the MX, so receiving-ip is meaningless), (2) the reporter aggregates across multiple receiving-ip values that all share the same (result-type, sending-mta-ip, receiving-mx-hostname) shape and chooses to omit the IP rather than pick one, (3) the reporter has privacy-policy constraints on IP disclosure. When receiving-ip IS present, it is the most precise identifier the operator has for which specific MX instance hit the failure — useful when the operator runs multiple MX backends behind the same hostname.
- What happens if a TLS-RPT report is delivered with a malformed JSON body?
- The receiver ingestion pipeline MUST treat the report as undeliverable and SHOULD log the parse failure for operator review. RFC 8460 does not specify a feedback channel back to the reporter — the reporter has no way to learn that its report failed to parse at the recipient. The practical consequence is that a malformed report from a specific reporter is silently lost; the operator will see a gap in coverage from that reporter. The most common malformed-report causes are (1) a truncated body where the HTTP POST was interrupted or the email attachment was truncated by a mailbox size limit, (2) a UTF-8 encoding issue where reporter-side string handling produced invalid byte sequences (rare but seen with operator-name fields containing non-ASCII characters), (3) an unexpected JSON shape from a reporter not strictly following RFC 8460. Robust ingestion pipelines log every parse failure with the full raw body so the operator can debug; DMARC Analyzer logs the failure and continues processing.
- How does a TLS-RPT report differ from a DMARC aggregate report?
- They are two distinct report formats covering different layers of email security. DMARC aggregate reports (RFC 7489 §7.2) describe SPF and DKIM authentication outcomes per (sending-source-IP, header-from-domain) bucket and are emitted by receiving MTAs to inform domain owners how their identity is being used. TLS-RPT reports (RFC 8460 §4) describe SMTP TLS session outcomes per (recipient-policy, sending-MTA, receiving-MX) bucket and are emitted by sending MTAs to inform recipient operators how their TLS policy enforcement is performing. DMARC reports are XML; TLS-RPT reports are JSON. DMARC reports cover message authentication; TLS-RPT reports cover transport security. Both are aggregated over 24-hour windows. Both can be delivered via mailto: and https:. Domains typically deploy both — DMARC RUA tells you "did this message come from the right sender", TLS-RPT tells you "did the connection use proper TLS". DMARC Analyzer ingests both and surfaces them in a unified dashboard.
Ingest TLS-RPT reports now
DMARC Analyzer ingests TLS-RPT JSON reports
and surfaces per-policy session counts and
per-failure result-type breakdowns in the
dashboard. Configure the
rua= destination to point at
your DMARC Analyzer mailbox or upload an
existing report file directly through the
dashboard.