v36.0
adbar/trafilaturav36.0Jul 25, 2026by pierre-b
AI Summary
Major update adding inline email attachments, fixing OIDC sign-in issues, and enhancing link click tracking and automation conditions.
Key Highlights
- Supports per-attachment inline images with content_id for transactional emails.
- Fixes SMTP implicit TLS (SMTPS) on port 465.
- Enhances link-click targeting for segments and automations by broadcast and URL.
- Renames email timeline events to standard dot notation (e.g., email.clicked).
New Features
- Inline email attachments
- OIDC email verification support
- Link click targeting by broadcast/URL
- Renamed email timeline events
Full Release Notes
- **Feature**: Transactional email attachments support a per-attachment `content_id` for inline images (e.g. per-recipient QR codes) — set `disposition: "inline"` plus `content_id` and reference the image from the template as `<img src="cid:...">`. Works across all email providers; inline images are assembled in a `multipart/related` MIME subtree on Amazon SES (previously they could show up as dangling attachments in Gmail/Outlook) and the SMTP provider now emits the RFC 2045 angle-bracketed `Content-ID` header that strict clients like Outlook require (#393)
- **Fix**: SMTP connections on port 465 now use implicit TLS (SMTPS). Workspace SMTP integrations, the setup wizard's "Test SMTP Connection", and system emails (magic codes, invitations, alerts) always spoke plaintext-first SMTP — waiting for a greeting that an SMTPS server never sends before negotiating STARTTLS — so a port-465 + TLS configuration hung or timed out. With TLS enabled and port 465, the client now performs the TLS handshake immediately after connecting and skips STARTTLS. TLS on any other port still negotiates STARTTLS, and `use_tls=false` remains plaintext, including on port 465. The TLS handshakes in the raw SMTP sender (both implicit and STARTTLS) are also now bounded by the dial timeout instead of hanging indefinitely on an unresponsive server (#385)
- **Fix**: OIDC sign-in no longer permanently fails with IdPs that omit the `email_verified` claim (Microsoft Entra ID, Cloudflare Access): an absent claim decoded as `false` and rejected every first login. Setting the new env-only `OIDC_ALLOW_UNVERIFIED_EMAIL=true` (default `false`) accepts first logins whose id_token omits the claim; an explicit `email_verified=false` is still always rejected, and the rejection log now names the flag to set (#388)
- **Fix**: Enabling OIDC through the setup wizard persisted `oidc_scopes="openid"` (the wizard has no scopes field), and that stored value overrode the `openid email profile` default at boot — authorize requests then lacked the email/profile scopes, so IdPs returned tokens without an email. The wizard and the settings screen now persist the full default when scopes are left empty, and the v36 system migration heals existing rows still holding the bare legacy value (when a row is healed the server restarts once so the change takes effect immediately) (#389)
- **Feature**: Segments and automation conditions can now target link clicks by broadcast and by URL. A "clicked email" timeline condition can be scoped to a specific broadcast and/or to a clicked link, where the link is matched as a case-insensitive substring of the destination URL (e.g. `/pricing` matches `https://acme.com/pricing?utm_campaign=summer`). This makes segments and automation filters like "clicked the pricing link in the Summer Sale broadcast" possible; it builds on the per-link click data already recorded in `message_history.clicked_links`, so no migration is needed (#339, #311).
- **Fix**: Viewing the contacts of a segment no longer returns a server error. The `segments.contacts` endpoint ordered by a `created_at` column that `contact_segments` does not have, so every request failed; it now orders by `matched_at` (when the contact joined the segment).
- **Fix**: Removed the "Photo URL" option from the automation *contact updated* trigger's field list — contacts have no photo-URL field, so an automation filtered on it could never fire.
- **Fix**: Segment/automation conditions that filter contact-timeline events by a field value now work. The query builder referenced a non-existent `metadata` column on `contact_timeline` (event fields live in the `changes` JSONB as `{field: {old, new}}`), so any such condition failed at SQL execution; it now reads the field's new value from `changes`.
- **Fix / Change**: The contact timeline now records email engagement events in the same dot notation as the rest of the timeline and the webhooks — `email.opened`, `email.clicked`, `email.bounced`, `email.complained`, `email.unsubscribed`, plus `email.sent` and `email.updated` — replacing the old `open_email` / `click_email` / `insert_message_history` / `update_message_history` names. This fixes automations triggered by an email engagement event, which previously never fired because the trigger listened for `email.clicked` while the timeline stored `click_email`. Workspace migration v36 renames existing timeline rows, rewrites stored segment definitions (both the tree and the compiled query) and regenerates the triggers of already-live automations, so segments and automations keep working without needing to be re-saved. The `email.sent` and `email.delivered` options were removed from the automation trigger list — they could never fire, and an `email.sent` trigger would loop an "every time" automation that sends email. (#339)
- **Security**: `golang.org/x/crypto` is pinned to 0.52.0 in both Go modules, closing the SSH-related advisories (12 critical/high) flagged by Dependabot.
- **Fix**: Automation email nodes now display the selected template's name on the canvas for non-marketing templates (e.g. `welcome`). The canvas resolved names from a reference list restricted to `category: 'marketing'`, while the node picker lets you select an email template of any category — so a validly selected non-marketing template fell back to the generic "Template set" label; the reference list is no longer category-restricted (#391).
- **Change**: Mailjet can now only be used as a transactional email provider, not a marketing one, because the Mailjet API overwrites unsubscribe links and breaks Notifuse's unsubscribe links. Newly assigning Mailjet as the marketing provider is rejected at settings-save, and marketing sends are refused at provider resolution — so workspaces that already had Mailjet assigned keep saving unrelated settings normally, but their broadcasts fail with a clear error until they switch the marketing provider.