feat: delivery preflight - detect bounces on prior sends before send-email - #1033
Merged
Conversation
…email
A 200 from POST /emails only means 'accepted'; the delivered/bounced outcome is
async, so a send logged email-sent can still have bounced (dead mailbox, spam
complaint) invisibly. Add scripts/check_email_bounces.py, run at the START of
send-email: it polls Resend GET /emails/{id} for every memory/email-log.json row
without a terminal delivery_status, writes the outcome back (delivery_status/
last_event/bounce/delivery_checked_at), flags a hard-bounced draft
status: contact-unverified, and prints BOUNCE_ALERT so the skill notifies the
operator. Advisory only (unset key / poll error no-ops). No new egress.
Regenerated eyebrowlock: send-email (this change) plus two pre-existing stale
hashes (skill-health, deploy-uni-hook) the drift gate requires refreshed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A
200fromPOST /emailsonly means Resend accepted the message - deliveredvs bounced is decided asynchronously. So a send logged
email-sent(http 200 +resend_id) can still have bounced (dead mailbox /
NoEmail, spam complaint)with the instance none the wiser.
What
At the start of
send-email, run a delivery preflight over earlier sends:scripts/check_email_bounces.py- polls ResendGET /emails/{id}(readsRESEND_API_KEYfrom env, never argv) for everymemory/email-log.jsonrowwithout a terminal
delivery_status, 30-day lookback.delivery_status(delivered / bounced / complained /failed / pending),
last_event,bounce(type/subType/message when present),delivery_checked_at.status: contact-unverified.BOUNCE_ALERT+ one line per new bounce; the skill then./notifys theoperator so a human handles the contact.
Advisory only - unset key, empty ledger, or a poll error just no-ops; never
blocks the send. Poll-based (fits aeon's cron/serverless model, no webhook endpoint).
Notes
send-email;disclosure-emaileris instance-local (aeon-vuln /aeon-onchain) and gets the same preflight there.
api.resend.com, already send-email's allowed host.send-email(this change) + two pre-existing stalehashes (
skill-health,deploy-uni-hook) that the drift gate requires refreshedin the same PR (content-hash only, no reach change).
eyebrow verify --ciclean.