# Email Spam Tester, in full Everything an agent needs to use this service without fetching another page. Short version at https://email-spam-tester.com/llms.txt ## What it does You send one real email to a disposable address. A mail server that behaves like a receiving one accepts it, and 41 checks run against what that server saw: the authentication chain, the sending infrastructure, two independent spam engines, the message structure, and the rules Gmail and Yahoo published for bulk senders in 2024. You get a score, every finding with its evidence, and a fix plan ordered by how many points each change is worth. The first 100,000 reports are free for everyone. No key, no account, no signup. The slug you get back is the capability: whoever holds it can read that report. ## The loop 1. `POST https://email-spam-tester.com/api/v1/inbox` Optional query parameters: `lang` (ISO code, decides the language of the fix plan and the per-check findings), `utm_source`, `utm_medium`, `utm_campaign`. ```json {"address": "test-@t.email-spam-tester.com", "slug": "", "expires_at": "2026-01-01T12:00:00Z"} ``` 2. Send the message over SMTP to that address. Not a summary of it, not a shortened version: the message that would go out, from the platform that would send it. The address accepts exactly one message and expires in an hour. 3. `GET https://email-spam-tester.com/api/v1/tests//status` A few hundred bytes. Poll this, not the report. ```json {"slug": "", "analysis_status": "checks_ready", "ai_status": "running", "checks_done": 41, "checks_total": 41} ``` 4. `GET https://email-spam-tester.com/api/v1/tests/` The report, once `analysis_status` is `checks_ready`. Several thousand lines. `analysis_status` runs `received`, `analyzing`, `checks_ready`, `failed`. `ai_status` runs `pending`, `running`, `ready`, `fallback`, `error`. The deterministic report is final at `checks_ready`; `ai_status` only ever adds the plan, so a caller that wants the checks does not wait for it. ## Over MCP instead `https://email-spam-tester.com/mcp`, streamable HTTP. Four tools: - `get_test_address(lang="en")` reserves one and returns the address and the report URL. - `wait_for_report(slug, timeout_seconds=150)` blocks until the analysis is done and returns a summary. Nothing to poll and no sleep loop to get wrong. - `get_report(slug)` reads an existing one without waiting. - `get_message_source(slug)` returns the message as delivered. The per-check lines these return are English whatever `lang` was set to. `lang` decides what the person opening `report_url` sees. ## Reading the report - `report_url` — the human-readable page. Hand a person this, not the JSON. - `score_ours` — 0 to 100, our model. Authentication and infrastructure carry most of the weight, because they decide delivery before a filter reads a word. - `score_compat` — 0 to 10, reproducing the SpamAssassin-style number, so the report is comparable to what somebody saw in an older tool. - `subscores` — the same 0 to 100 scale for `auth`, `infra_spam`, `content`, `compliance`. - `complete` — false when a check could not run. An unchecked item is never a pass; treat the score as optimistic until this is true. - `errored_checks` — the ids of those. - `checks[]` — all 41. Each has `id`, `category`, `status`, `title`, `summary`, `summary_localised`, `evidence`, `weight_compat`, `weight_ours`, `citations`. - `checks[].citations` — `standards` (the RFC section, with the sentence quoted verbatim), `receivers` (Google's own page), `further`. Curated by hand rather than generated. - `fixes[]` — ordered. Each has `fix` (title, severity, what to change), `gain_compat`, `gain_ours`, `source`. Gains are computed by re-scoring the report, one fix at a time, so they do not compose: the honest number is the second report. - `ai_verdict` — one paragraph on what a filter would make of the message. - `message` — subject, from, bounce address, size, whether it has HTML and text parts, and which headers the sender forged. `summary` is always English so a script reads the same words every time. `summary_localised` carries the translation when the report has a language and the translation passed verification; it is null far more often than not. ## Check statuses - `pass` — ran, found nothing wrong. - `warn` — worth fixing, costs points. - `fail` — will cost delivery, costs more points. - `skip` — did not apply. No attachments to scan, no HTML part to weigh. **Not a pass.** - `error` — could not find out. Excluded from the score and flagged, rather than counted as a pass. Act on `fail` first, then `warn`. One `fail` in authentication outranks thirty passes elsewhere. ## What the checks cover Authentication: SPF, Sender ID, DKIM signature, DKIM alignment, SPF alignment, DMARC result, ARC chain. Infrastructure and reputation: reverse DNS and whether it forward-confirms, the HELO name, MX records, whether the sending hostname resolves, transport encryption, two dozen blocklists. Spam engines: SpamAssassin, Rspamd, an AI judge, and a combined verdict. A Postmark check exists and ships switched off, because it would send the whole message to a third party. Content: text and HTML parts, the balance between them, HTML size, image alt text, script and iframe tags, shortened links, whether links resolve, image weight, the subject line, the preheader, link reputation, attachment scanning. Bulk sender rules: List-Unsubscribe, one-click unsubscribe, a published DMARC policy, TLS for bulk mail. Advisory, never scored: BIMI, MTA-STS, TLS reporting, DNSSEC, DKIM key age and length. ## What a message cannot fix Some findings are DNS, not content. Say so plainly instead of editing the draft until the score moves: - SPF, DKIM and DMARC records, and whether they align with the From domain - reverse DNS for the sending IP, and whether it forward-confirms - MX records, TLS on the sending host, blocklist entries - MTA-STS, TLS reporting, DNSSEC, BIMI Everything else is the draft: subject, preheader, HTML weight, the text part, image alt text, link shorteners, unsubscribe headers. ## Errors - `202` — reserved, nothing has arrived. The body carries the address, the expiry and the language, so a caller that did not create the reservation can still show it. - `404` — no such slug. - `410` — the address expired before a message arrived. - `409` — tried to change the language after the message had arrived. - `429` — the free-test limit for that address. Not enforced while the free allowance lasts. ## Habits worth keeping Test the message you are actually going to send; a rewritten sample measures the sample. Send it from the platform, not from a personal account. Re-test after fixing, because the expected gains do not compose. And do not test somebody else's mail without asking: the report shows the subject, the sender, the bounce address and the full source to anyone holding the link. ## Languages 31, under a language prefix: `https://email-spam-tester.com/de/`, `/ru/`, `/ja/` and so on. Documentation at `//docs/`, the agent page at `//skill/`, this API reference at `//api-docs/`.