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-type is one of sts, tlsa, or no-policy-found; policy.policy-string reproduces the evaluated policy verbatim; policy.policy-domain and policy.mx-host identify the scope.
  • Summary: summary.total-successful-session-count and summary.total-failure-session-count — both counts are per SESSION, not per message.
  • Failure details: failure-details[] array of per-failure-shape records. result-type classifies the failure into one of 12 canonical values; failed-session-count aggregates sessions with the same (result-type, sending-mta-ip, receiving-mx-hostname) shape.
  • 12 canonical result-type values: 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 whose report-id has 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-datetime is the inclusive start of the window; end-datetime is 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: sts means the sender evaluated an MTA-STS policy (RFC 8461). tlsa means the sender evaluated a DANE TLSA record (RFC 7672). no-policy-found means neither applied — the session used opportunistic TLS or plaintext. Each value implies a different shape for the policy-string field.
policy.policy-string
Type: array of strings.
Required: yes (may be empty for no-policy-found).
Semantics: the verbatim policy that was evaluated, reproduced line-by-line. For policy-type=sts this is the per-line content of the retrieved mta-sts.txt file (e.g., ["version: STSv1", "mode: enforce", "mx: mail.example.com", "max_age: 604800"]). For policy-type=tlsa this is the TLSA resource-record values formatted as strings. For policy-type=no-policy-found this 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 the To:-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. For policy-type=sts this is one of the mx: 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 all failure-details[].failed-session-count entries in the same policy. Verifying that the sum matches the total-failure-session-count is 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 to receiving-mx-hostname but 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 complementing result-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+json payload. 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.

  1. Counting failed-session-count as messages — the field is per SESSION, not per message. A dashboard labelling the count as "failed messages" misleads operators; label as "failed sessions".
  2. 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 the report_id column.
  3. Ignoring policy-type=no-policy-found — pipelines that filter for sts+tlsa only lose the opportunistic-TLS visibility signal. Index every policy-type.
  4. Not gunzipping application/tlsrpt+gzip — parsing the gzip-compressed body as JSON produces a parse error. Inspect the body's first two bytes (gzip magic 1F 8B) before parsing.
  5. Conflating sending-mta-ip with From: header domain — they are different identities. sending-mta-ip is the IP address of the connecting sending MTA, not the message-author identity.
  6. Ignoring failure-details=[] reports — successful aggregation windows produce empty failure-details arrays. These are NOT errors; they are the baseline "everything was fine" data operators need for trend analysis.
  7. Treating certificate-error as cert-trust failure — certificate-error is the legacy catch-all category. Inspect additional-information / failure-reason-code for the specific cause before assigning a remediation.
  8. Hard-failing on unknown result-type values — future RFC updates may add new result-type values. Robust ingestion pipelines log unknown values but store the report anyway under an "other" category.
  9. Not validating that summary.total-failure-session-count equals the sum of failure-details[].failed-session-count — mismatches indicate either a reporter bug or a truncation during delivery. Worth a log warning.
  10. Storing policy-string as 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.

Sign up — Free plan Audit my TLS-RPT record