Content scanning and held emails

Groundmail scans the content of outgoing and incoming email for spam. Outgoing scanning protects the sending IPs your mail shares with other customers; incoming scanning gives every received email a spam score you can act on.

Outgoing email

Every email sent from a Groundmail mail server is scored before it leaves. A score is the sum of many content checks (suspicious links, phishing patterns, known spam phrases, broken formatting). Normal transactional and product email scores well under 5.

Who What happens over the limit Limit (transactional / product)
Accounts new to sending (the first stages of the ramp-up, or new Free accounts), and accounts we are reviewing The email is held for review before it is sent 12 / 7
Every account The email is rejected and never sent 30 / 15

Transactional email (the default category) has a higher limit than product email, so sign-in codes and receipts are not stopped by a borderline score.

Held emails

A held email has status held:

  • POST /emails returns "status": "held" instead of success.
  • The email shows Held on the Emails page.
  • An email.held webhook is sent with held_reason and spam_score.

We review held emails quickly. Released email is sent as usual and you get email.delivered (or a bounce). Rejected email is never sent and you get email.failed with the reason in bounce_reason.

An email can also be held while sending is paused on your account. It goes out when sending resumes.

Rejected emails

An email over the rejection limit gets status failed, an email.failed webhook, and a bounce_reason like Rejected by content scanning: spam score 31.2 is at or over the limit of 30. The recipient is not added to your suppression list: the address did nothing wrong.

Repeated holds or rejections in a day pause sending on the account while we look at it, the same way a high bounce rate does.

Avoid false positives

  • Send from a domain with SPF, DKIM and DMARC set up (Groundmail sets these up when you verify a domain).
  • Include a plain-text version, and keep HTML well formed.
  • Link to your own domain, not URL shorteners.
  • Avoid all-caps subjects, piles of exclamation marks and image-only email.

If a legitimate email was held or rejected, reply to the notification or write to support with the email id (msg_…).

Incoming email

Email received on an inbound domain carries two fields, on the received email and in the email.received webhook:

Field Values
spam_score A number. Higher is more likely spam; most clean mail scores under 5. null when the message was not scanned.
spam_status clean (under 5), suspicious (5 up to the spam threshold), spam (at or over the threshold, 10 by default). null when not scanned.

Filter spam per domain

Set a threshold and an action on the domain:

curl -X PATCH https://api.groundmail.dev/domains/$DOMAIN_ID \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"inbound_spam_threshold": 8, "inbound_spam_action": "drop"}'
await groundmail.domains.update('example.com', {
  inbound_spam_threshold: 8,
  inbound_spam_action: 'drop',
})
groundmail domains update example.com --inbound-spam-threshold 8 --inbound-spam-action drop
  • inbound_spam_action: "flag" (the default) keeps the email and marks it spam_status: "spam".
  • inbound_spam_action: "drop" discards email at or over the threshold: nothing is stored and no webhook is sent.
  • inbound_spam_threshold: null goes back to the default (10) and drops nothing.