Skip to main content

Email Grader API

The Email Grader scores deliverability across five deterministic dimensions:

  • DNS health — MX redundancy, A records, SPF/DKIM/DMARC presence.
  • Authentication — SPF strictness, DKIM key sizes, DMARC enforcement.
  • Spam likelihood — Bayesian, header, content, and URL signals from the shared spam-filter engine.
  • Content quality — Trigger phrases, ratio of links, HTML hygiene.
  • Reputation — Lookups against the curated domain blocklist.

Every score is computed from observable signals; there is no opaque ML model in the grading path.

Endpoints

MethodPathAuthDescription
POST/v1/grader/checkPublic (rate-limited)Domain-only check from DNS
POST/v1/grader/submitAPI key (grader:write)Full email analysis, persisted
GET/v1/grader/results/{id}API key (grader:read)Fetch a stored result
GET/v1/grader/historyAPI key (grader:read)List recent results, paginated

Domain check (public)

Rate-limited per source IP. The default limit is 10 requests per hour and is configurable per-deployment.

curl -X POST https://api.apexmail.ee/v1/grader/check \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.com",
    "selectors": ["default", "google", "selector1"]
  }'

Response (truncated):

{
  "domain": "example.com",
  "score": 84,
  "grade": "A",
  "breakdown": {
    "dns_health": { "score": 90, "max": 100 },
    "authentication": { "score": 80, "max": 100 },
    "spam_likelihood": { "score": 90, "max": 100 },
    "reputation": { "score": 100, "max": 100 }
  },
  "findings": [
    { "severity": "info", "category": "dns", "message": "Only one MX record — add a backup MX for redundancy" }
  ],
  "recommendations": [
    "Email authentication is well configured — consider adding BIMI and MTA-STS for enhanced security"
  ]
}

Submit email (authenticated)

Submit a complete email envelope for scoring. Results are persisted under your tenant. Request size is capped (default: 256 KB combined body).

curl -X POST https://api.apexmail.ee/v1/grader/submit \
  -H "X-API-Key: $APEXMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "hello@example.com",
    "to": ["user@example.org"],
    "subject": "Welcome to ApexMail",
    "body_text": "Thanks for trying ApexMail!",
    "headers": { "List-Unsubscribe": "<mailto:unsub@example.com>" },
    "selectors": ["default"],
    "sender_ip": "203.0.113.10",
    "helo_hostname": "mail.example.com",
    "mail_from": "bounce@example.com"
  }'

When sender_ip and helo_hostname are supplied, the grader runs the same SPF/DKIM/DMARC pipeline as the production MTA against the submitted message. Without them, only DNS-readiness scoring is used and a finding is emitted to make that explicit.

The response includes a created_at timestamp and a stable id you can use to retrieve the result later.

Retrieve a stored result

curl https://api.apexmail.ee/v1/grader/results/{id} \
  -H "X-API-Key: $APEXMAIL_API_KEY"

Returns 404 if the result does not belong to the calling tenant — results are strictly scoped per-tenant at the database layer.

List recent results

curl "https://api.apexmail.ee/v1/grader/history?page=1&per_page=20" \
  -H "X-API-Key: $APEXMAIL_API_KEY"

per_page is clamped to [1, 100].

Required scopes

EndpointScope
POST /v1/grader/submitgrader:write
GET /v1/grader/results/{id}grader:read
GET /v1/grader/historygrader:read

The wildcard scope * grants all of the above.

Errors

All errors follow the standard ApexMail envelope:

{ "error": { "code": "INVALID_INPUT", "message": "subject contains CR/LF" } }
HTTPCodeMeaning
400INVALID_INPUTMissing or malformed fields
400INVALID_TENANTAuth context missing valid tenant UUID
403INSUFFICIENT_SCOPEAPI key missing the required scope
404NOT_FOUNDResult not found in your tenant
413BODY_TOO_LARGECombined body exceeds the configured cap
429RATE_LIMITEDPublic check rate limit exceeded
500PERSIST_FAILED / DB_ERRORDatabase write or read failure
503GRADER_DISABLEDGrader is disabled in this deployment

Limits and configuration

SettingDefaultEnv var
Public rate limit10 / hour / IPGRADER_RATE_LIMIT, GRADER_RATE_WINDOW
Max body size256 KBGRADER_MAX_BODY_SIZE
Domain cache TTL5 minGRADER_CACHE_TTL
Default DKIM selectorsdefault,google,dkim,selector1GRADER_DKIM_SELECTORS
Auth hostnamegrader.apexmail.eeGRADER_AUTH_HOSTNAME