Repository navigation
1.9.0: coupons@profullstack.com intake (Forward Email webhook) - #76
Merged
Merged
Conversation
POST /api/webhooks/email takes Forward Email's parsed-message JSON for the coupons@ alias (auth: ?key=INBOUND_EMAIL_SECRET, or X-Webhook-Signature when FORWARDEMAIL_WEBHOOK_KEY is set). Each message is refused when neither DKIM nor SPF passed, its subscription-confirm link is followed when it is a double opt-in mail (never unsubscribe/preferences links, never private addresses), or its offers are extracted (claude-haiku-4-5 with structured output, rules fallback), checked (a code must appear verbatim in the mail), deduped against existing codes at the same store, and posted with source 'email'. Every message is logged in inbound_emails with its outcome; GET on the same route and `c0upons inbox` read the log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| const key = messageKey(mail); | ||
| const result: InboundResult = { ok: true, outcome: 'no-offer', message_key: key, posted: [], duplicates: [], skipped: [], clicked: [] }; | ||
|
|
||
| const prior = await db.sql`SELECT outcome FROM inbound_emails WHERE message_key = ${key} LIMIT 1`; |
| test('a code already posted by hand under a differently named store is a duplicate', async () => { | ||
| await db.sql`INSERT INTO stores (name, slug, website) VALUES ('The Proof Peptides', 'the-proof-peptides', 'https://theproof.com')`; | ||
| const [{ id }] = await db.sql`SELECT id FROM stores WHERE slug = 'the-proof-peptides'`; | ||
| await db.sql`INSERT INTO coupons (store_id, code, title) VALUES (${id}, 'PROOF50', 'PROOF50')`; |
ThreatCrush Security Scan31 finding(s) MEDIUM: 28 | LOW: 3
Snippets are redacted; ThreatCrush never prints matched credential material. |
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.
What
Mail to coupons@profullstack.com becomes coupons on c0upons.com.
POST /api/webhooks/emailreceives Forward Email's webhook JSON (the parsed message plus DKIM/SPF/DMARC verdicts) for the alias. For each message it:claude-haiku-4-5structured output, falling back to the Reddit source's rules (extractCode,parseDiscount) when there is no key or the model refuses. A code only counts if it appears verbatim in the mail. An offer is usable if it has a code, or a discount and a link.source 'email',source_id '<message hash>:<n>'.inbound_emailstable with its outcome: posted, duplicate, confirmed, confirm-failed, no-offer, unauthenticated, ignored or error.Auth is
?key=$INBOUND_EMAIL_SECRETin the alias's webhook URL, orX-Webhook-SignaturewhenFORWARDEMAIL_WEBHOOK_KEYis set. With neither set, every request gets a 401, so this is safe to deploy before the secret exists.Surfaces: API (
GET /api/webhooks/emailwith Bearer secret lists the log), the shipped shell CLI and the TS CLI (c0upons inbox).Ops still needed
INBOUND_EMAIL_SECRETin~/www/c0upons.com/app.envon dev2 + vaultc0upons--prodcoupons@profullstack.com→https://c0upons.com/api/webhooks/email?key=<secret>(dashboard only: no API token exists)Tests
test/inbound-email.test.mjs(11 cases): signature check, rules, a code the model made up gets dropped, post then reprocess then duplicate, a cross-store duplicate of a hand-posted code, no-offer, forged mail, opt-in click (and not the unsubscribe link), promo "verify" link not clicked, private-address refusal. Full suite: 70 pass; typecheck clean.🤖 Generated with Claude Code