Check our work.
Three pieces of code that prove what happens to your email data. This is the actual code running on our servers.
Lines marked [redacted — proprietary] contain scoring logic that has no bearing on data handling. Every line that touches your email data is shown in full.
1. What we store
The only database table that records anything about your emails.
This is the filter_log table schema. Every column is listed.
There are no other tables that store email data.
CREATE TABLE IF NOT EXISTS filter_log ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), timestamp TEXT NOT NULL, verdict TEXT NOT NULL ); -- Possible verdicts: 'junk', 'review', 'legit', 'whitelist' -- No subject, sender, body, score, or reason column exists.
1
Four columns. user_id (an integer — not your email address),
timestamp, and verdict. That's the entire record for every email ever processed.
No subject, no sender, no content.
2. What goes to Anthropic
The exact API call made to score each email.
The system prompt contains proprietary scoring logic and is redacted.
The user message — what actually contains your email data — is shown in full.
rel_msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=200,
system=RELEVANCE_PROMPT,
messages=[{
"role": "user",
"content": f"Sender: {sender}\n"
f"Subject: {subject}\n\n"
f"Body:\n{body[:1500]}"
}],
)
# Only the score is kept from the response
rel_score = rel.get("relevance_score", 50) # a number, 0-100
# sender, subject, and body are not stored after this point.
1
body[:1500] — only the first 1,500 characters of the email body
are sent to Anthropic. The rest is never read.
2
rel_score — a single integer is all that comes back and all
that is retained. The full response object is discarded immediately.
3
Anthropic's retention: API request data is held for up to 7 days
for abuse monitoring then permanently deleted. Never used for model training.
See Anthropic's privacy policy.
3. What gets written to the database
The function that writes the result of filtering an email.
This function accepts subject, sender, and scores as arguments.
None of them are written to the database.
def _log_filter_result(
user_id,
subject, # received — never stored
sender, # received — never stored
verdict,
rel_score, # received — never stored
ai_score, # received — never stored
reason # received — never stored
):
"""
Only verdict and timestamp are written.
The operator has zero knowledge of inbox contents.
"""
ph = _ph()
conn = get_db()
_execute(conn,
f"INSERT INTO filter_log (user_id, timestamp, verdict)"
f" VALUES ({ph}, {ph}, {ph})",
(user_id,
datetime.now(timezone.utc).isoformat(),
verdict)
)
conn.commit()
conn.close()
1
7 arguments received, 3 written. subject, sender, rel_score,
ai_score, and reason are accepted by the function and immediately discarded.
Cross-reference with the schema above — no columns exist to store them in anyway.
2
This function is called as
_log_filter_result(user_id, None, None, verdict, 0, 0, "") —
subject and sender are explicitly passed as None, making it
structurally impossible for them to be stored.