The verdict
Copy MarkdownThe verdict
A verdict is one JSON document per received message, with the schema name sendscore.verdict/v1. The MCP tools, the API, and sendscore check --json return the same document. Within a schema version a field is never renamed or given a new meaning; new fields may be added.
An example
An example, trimmed to a few of its checks. DKIM passes, but the signature belongs to the provider’s domain, so DMARC does not align, where it did on the previous issue.
{
"schema": "sendscore.verdict/v1",
"id": "rpt_8f2k1q9d",
"message": {
"received_at": "2026-10-28T13:12:04Z",
"from": "Daily Photo Tips <tips@dailyphototips.com>",
"subject": "Shooting into the sun",
"message_id": "<abc123@kit-mail.com>",
"size_bytes": 48211
},
"verdict": "fail",
"ready_to_send": false,
"summary": "1 failure and 1 warning. DMARC does not align.",
"top_fixes": [
{
"check": "auth.dmarc",
"who": "you",
"fix": "Set up DKIM with your own domain (dailyphototips.com) in your email platform, so messages are signed as you rather than as the platform."
},
{
"check": "changes.regressions",
"who": "you",
"fix": "Something changed since the last issue. Check recent template, DNS, or platform changes."
}
],
"checks": [
{
"id": "auth.dkim",
"category": "auth",
"status": "pass",
"title": "DKIM signature verified",
"evidence": {
"signatures": [
{ "d": "kit-mail.com", "s": "k1", "a": "rsa-sha256", "result": "pass", "aligned": false, "key_bits": 2048, "key_type": "rsa" }
]
},
"docs": "https://support.google.com/mail/answer/81126"
},
{
"id": "auth.dmarc",
"category": "auth",
"status": "fail",
"title": "DMARC does not align",
"detail": "Neither a passing DKIM signature nor a passing SPF check uses the From domain dailyphototips.com.",
"evidence": {
"from_domain": "dailyphototips.com",
"policy": "none",
"spf_aligned": false,
"dkim_aligned": false
},
"fix": {
"who": "you",
"text": "Set up DKIM with your own domain (dailyphototips.com) in your email platform, so messages are signed as you rather than as the platform."
},
"docs": "https://www.rfc-editor.org/rfc/rfc7489"
},
{
"id": "content.links",
"category": "content",
"status": "skipped",
"title": "Links not checked",
"reason": "Link checking is off."
},
{
"id": "changes.regressions",
"category": "changes",
"status": "warn",
"title": "Worse than the previous issue: auth.dmarc (pass to fail)",
"evidence": { "changes": ["auth.dmarc (pass to fail)"] },
"fix": {
"who": "you",
"text": "Something changed since the last issue. Check recent template, DNS, or platform changes."
}
}
],
"changes": [
{ "field": "auth.dmarc", "before": "pass", "after": "fail", "worsened": "auth.dmarc" },
{ "field": "dkim.d", "before": "dailyphototips.com", "after": "kit-mail.com" }
],
"fingerprint": {
"dkim_domains": ["kit-mail.com"],
"dkim_selectors": ["k1._domainkey.kit-mail.com"],
"return_path_domain": "bounce.kit-mail.com",
"remote_ip": "203.0.113.10",
"ptr": "mta10.kit-mail.com",
"list_unsubscribe": true,
"one_click": true,
"link_hosts": ["dailyphototips.com", "kit-mail.com"],
"html_bytes": 41022,
"links": 14,
"images": 3,
"auth": { "dkim": "pass", "dmarc": "fail", "spf": "pass" }
},
"explanation": null,
"note": "Receipt at SendScore does not prove inbox placement at Gmail or any other provider."
}Fields
| Field | What it holds |
|---|---|
schema |
Always sendscore.verdict/v1 for this version. |
id |
The report’s id, when the app stored it (rpt_ and a token). The CLI leaves it out. |
message |
received_at, from, subject, message_id, and size_bytes. |
publication |
The newsletter it belongs to, when there is one. Left out otherwise. |
verdict |
pass, warn, or fail, for the message as a whole. |
ready_to_send |
Whether nothing in a blocking category failed, and DKIM, DMARC, and the unsubscribe signature could be evaluated. |
summary |
The counts of failures and warnings, and the titles of the first two failures (or warnings, when nothing failed). It is "Every check that ran passed." when nothing failed or warned. When authentication could not be evaluated, a sentence saying so comes first. |
top_fixes |
At most three fixes, each with the check it fixes, who fixes it, and the fix. |
checks |
Every check, with its id, category, status, and title. Each may add detail, evidence, a fix (with who and text), and a docs link; a skipped check adds reason. A list in evidence keeps its first 20 items, and when there were more, the full count is beside it under the same key with _total added. |
changes |
What changed since the previous issue. Always present, and empty when there is nothing to compare with. |
fingerprint |
What the comparison looks at, stored with every verdict so the next issue can be compared without keeping this message. |
explanation |
Room for an optional plain-language write-up. The engine never fills it, so it is null. |
note |
A reminder that receipt at SendScore does not prove inbox placement. |
Check categories are auth, unsub, struct, content, and changes. See the checks for every id.
The overall verdict
failif any check fails.- Otherwise
warnif any check warns, ifauth.dkimorauth.dmarcwas skipped, or if the check was stopped before it finished. - Otherwise
pass.
Skipped checks don’t count otherwise, so a pass means every check that ran passed. The CLI’s exit code follows the verdict, with 0 for pass, 1 for warn, and 2 for fail.
ready_to_send
ready_to_send is true unless a check in a blocking category fails. The blocking categories are authentication (auth) and unsubscribe (unsub).
It is also false when auth.dkim or auth.dmarc is skipped, or when unsub.signed is skipped because the signature that decides it could not be verified. Then nothing shows the message is authenticated, so the verdict is at least warn. The same goes for a check that was stopped before it finished, whose summary is "The check was stopped before it finished, so this is not ready to send."
- When DNS lookups failed or the CLI ran with
--offline, the summary starts with "Authentication could not be checked because DNS lookups failed, so this is not ready to send.", and the first fix is to run the check again once DNS lookups work. - When nothing failed in DNS but a result was still hidden, the summary starts with "Authentication could not be fully checked, so this is not ready to send." This happens when a saved file or an upload has no envelope and its Return-Path domain aligns with the From domain, or when a DKIM signature expired after the message was received. There is no fix to run it again, since running it again would give the same answer. For a saved file, pass the envelope to the CLI.
So a message can have the verdict fail and still be ready to send, when the only failure is editorial, such as a broken link. An agent should read ready_to_send to decide whether to send, and verdict to decide whether to tell someone.
top_fixes
At most three, in order. When auth.dkim or auth.dmarc was skipped because DNS lookups failed, the fix to run the check again comes first. After it come failures in a blocking category, then other failures, then warnings. A fix with the same text as one already chosen is left out. Each has the check it fixes, who fixes it (you or provider), and the fix itself.
changes
Each change has the field that changed and its value before and after. Every change in an authentication result is listed, including a fix. When it made a check worse, worsened names the check, and the changes.regressions check warns. An authentication result that went from pass to skipped is a change with no worsened, since it was not checked rather than broken. The fields are auth.spf, auth.dkim, auth.dmarc, dkim.d, dkim.selector (with selectors in the same selector._domainkey.domain form as the fingerprint), return_path.domain, sending.ip, sending.ptr, unsub.list_unsubscribe, unsub.one_click, links.hosts (with the hosts removed as before and those added as after), html.bytes, links.count, and images.count.
fingerprint
| Field | What it holds |
|---|---|
dkim_domains, dkim_selectors |
The d= domains of the DKIM signatures, and their selectors, each written as the full key name, such as k1._domainkey.kit-mail.com |
return_path_domain |
The envelope sender’s domain |
remote_ip, ptr |
The sending IP and its verified reverse DNS name, when known |
list_unsubscribe, one_click |
Whether the message had List-Unsubscribe, and working one-click unsubscribe |
link_hosts |
The hosts the message links to |
html_bytes, links, images |
The HTML size, and the counts of links and images |
auth |
The spf, dkim, and dmarc statuses. The CLI leaves them out of the previous issue’s fingerprint, so diff and --previous do not compare them |