v33.0
The-Vibe-Company/megaparsev33.0Jun 19, 2026by pierre-b
AI Summary
Adds 'Stop-on-reply' functionality for automations, supports multiple root administrators, and fixes translation preview text issues.
Key Highlights
- New 'Stop-on-reply' feature for automations (Mailgun & SES)
- Support for multiple ROOT_EMAIL addresses
- Fixes for translated email template preheaders
- Database schema updates for reply matching infrastructure
New Features
- Stop-on-reply for automations
- Multi-root administrator support
Full Release Notes
### Features
- **Feature**: Stop-on-reply for automations (#346). When a contact replies to a sequence email, the journey stops. Replies are ingested via a new public endpoint `POST /webhooks/email/inbound?workspace_id={id}&integration_id={id}` (Mailgun first; provider-agnostic parser registry for the rest). For Mailgun, the inbound Route that forwards replies to this endpoint is now created automatically by the same **Register Webhooks** action used for delivery/bounce webhooks (only the DNS MX records remain a manual step); the route is non-preemptive (no `stop()`, lower priority) so it never silently overrides other inbound consumers on a shared Mailgun domain. The public endpoint is rate-limited per source IP and per workspace, and permanent client errors return 4xx (not 5xx) so providers don't retry-loop. Inbound mail is classified so bounces and out-of-office auto-replies never count as a reply. A genuine reply is matched to the send strictly by `In-Reply-To`/`References` → the `Message-ID` stored at send time (persisted before the email is dispatched so even an instant reply matches), which both identifies the contact and scopes the exit to the exact automation that sent the replied-to email; replies that don't match a stored send are ignored, and a replayed/duplicate reply is deduplicated so it can't re-exit a re-enrolled journey. The matched reply is recorded on the contact timeline as `email.replied` and — when the automation has **Exit on reply** enabled — exits the contact's journey (bounded to journeys entered before the reply). The stop is enforced by a layered guarantee (event-driven interrupt + an active-guarded optimistic lock on the executor's happy *and* error paths + a just-in-time guard in the email queue worker) so it holds whether the contact is mid-delay or the next email is already queued, and a concurrent exit is never resurrected by a retry. The feature is free for workspaces that don't enable it (no extra per-send queries or index maintenance).
- **Feature**: Stop-on-reply also supports **Amazon SES** — inbound replies arrive as RSA-signature-verified, topic-bound Amazon SNS notifications; **Register Webhooks** auto-provisions the SNS topic + a coexistence-safe SES receipt rule (scoped to verified identities, region-validated), and the Message-ID SES returns at send is captured and matched host-independently (#346).
- **Feature**: `ROOT_EMAIL` now accepts multiple comma/semicolon-separated emails, so a shared self-hosted instance can have more than one root administrator without displacing the first. Root-gated actions (workspace creation, system settings, root HMAC sign-in) now check list membership instead of a single equality, and a user row is created on startup for every listed root so each can sign in immediately. Matching is case-sensitive and a single email behaves exactly as before. The console System Settings drawer edits the list as tags; the setup wizard still establishes the primary (first) root. Example: `ROOT_EMAIL=alice@example.com,bob@example.com` (#361).
### Fixes
- **Fix**: Translated email templates now send with their own inbox preview text (preheader) instead of the default language's. The inbox preview is rendered from the `mj-preview` block embedded in the email tree, but the metadata-sync that stamps it from the `subject_preview` field only ran for the default template — never for translations — so a translation kept the preview value it was cloned with, even after its preview was edited and saved. The sync now stamps every language variant, and all send paths (broadcast, automation, transactional) additionally inject each resolved variant's `subject_preview` at compile time, which also corrects already-saved translations without a re-save (#359).
### Database Schema Changes
- Migration v33.0 (workspace): adds `message_history.smtp_message_id` (with a partial index) for reply matching, `automations.exit_on_reply`, a partial unique index on `inbound_webhook_events` to dedup replayed replies, and redefines the `track_inbound_webhook_event_changes()` trigger so inbound events of type `reply`/`auto_reply` surface on the contact timeline as `email.replied`/`email.auto_reply`. Column adds are nullable/constant-default (instant, no table rewrite).