feat(sbat): inbound email webhook for support tickets#76
Open
Conversation
Add MailerSend-compatible webhook endpoint at /api/sbat/inbound-email that receives inbound emails and creates support tickets. Tickets are stored in a new support_tickets table and posted to #subwaybuilder-sbat Discord channel for team visibility. Endpoints: - POST /api/sbat/inbound-email — receive MailerSend webhook - GET /api/sbat/tickets — list tickets (filterable by status) - GET /api/sbat/tickets/:number — get single ticket - PATCH /api/sbat/tickets/:number — update status/assignee/priority Features: - Auto-generates ticket numbers (SBAT-0001, SBAT-0002, ...) - Auto-detects priority from subject/body keywords - Verifies MailerSend webhook signature (SBAT_WEBHOOK_SECRET) - Posts formatted ticket summary to Discord channel - Supports both MailerSend and generic JSON payload formats Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Require SBAT_WEBHOOK_SECRET (reject if unconfigured) - Fix timingSafeEqual crash on length-mismatched signatures - Prefer raw body for signature verification over re-serialized JSON - Add rate limiter (10 requests/minute) on inbound endpoint - Validate status/priority enums on PATCH (whitelist only) - Validate ticket number format (SBAT-NNNN) on GET/PATCH - Validate sender email format on inbound - Truncate inputs (subject 500, body 50k, name 200, assignee 100) - Strip raw payload from stored metadata (no PII echo) - Remove error details from 500 responses (no internal leaks) - Exclude body_text/body_html from list endpoint (summary only) - Log structured update fields instead of raw req.body Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
express.json() discards the raw buffer by default, so rawBody was always undefined and signature verification fell back to re-serialized JSON (which may not match what MailerSend signed). Add verify callback to capture the original buffer when the MailerSend signature header is present. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SBAT has its own app and DB on Vercel. Coach Artie is just the relay between MailerSend and the Subway Builder Discord channels. Removed: - support_tickets DB table and schema - Ticket CRUD endpoints (GET/PATCH /tickets) - Ticket number generation - getSyncDb import Kept: - POST /api/sbat/inbound-email — validates + relays to Discord - Signature verification, rate limiting, priority detection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
/api/sbat/inbound-emailwebhook endpoint for receiving MailerSend inbound emails as support ticketssupport_ticketsDB table with auto-incrementing ticket numbers (SBAT-0001, etc.)#subwaybuilder-sbatDiscord channel with priority emoji and formatted previewEndpoints
/api/sbat/inbound-email/api/sbat/tickets?status=open)/api/sbat/tickets/:number/api/sbat/tickets/:numberFiles changed
packages/capabilities/src/routes/sbat.ts— new route file (296 lines)packages/capabilities/src/index.ts— mount route at/api/sbatpackages/shared/src/db/schema.ts—support_ticketstable definitionTest plan
SBAT_WEBHOOK_SECRETenv var when MailerSend is set uphttps://capabilities.tools.ejfox.com/api/sbat/inbound-email🤖 Generated with Claude Code