🔄 Automatic Update Notifications
MAILER_SMTP_FROM_EMAIL
is now used as the default forMAILER_SMTP_USER
when onlyMAILER_SMTP_FROM_EMAIL
is set. Before, the inverse was true (onlyMAILER_SMTP_USER
needed to be set and was used as the default forMAILER_SMTP_FROM_EMAIL
). This change was made to address the fact that the SMTP username might be confidential and shouldn’t be accidentally exposed by using it as the FROM address in system emails. If you are already setting both environment variables, you don’t need to change anything but if you were only settingMAILER_SMTP_USER
, you need to update your configuration. Fixes #356 (thanks @kevinam99 for implementing)
- Information about new releases is now automatically fetched and displayed on a new "System info"
admin page. You can disable this by setting
DISABLE_UPDATE_CHECKS=true
. - The log level can now be configured by setting
LOG_LEVEL
todebug
,info
, andwarning
. Implements #360 (thanks @kevinam99) mjml_body
anhtml_body
are now included in the Campaign API responses
- Segment queries with the
$not
operator (e.g.{"$not": {"data.foo": "bar"}}
) now also correctly include entries where the queried data keys don't exist at all. - Fixed possible exception when removing "Reply To" setting from sender settings.
- Fixed Campaign API docs to include
json_body
as a map instead of a string.
- Button for creating new users as admin (thanks @pmareke and @hellehata)
- Contacts can now re-subscribe or update their data by submitting a form again (thanks @lislegaard for suggesting)
- Honeypot fields to block bot subscriptions
- Limited
first_name
andlast_name
fields to 50 characters. Implements #342 (thanks @lislegaard for suggesting)
- Updated broken tzdata library. Fixes #336. (thanks @VincentSC for reporting)
- Deletion of shared senders from the UI is now working. Fixes #326 (thanks @bpivk for reporting)
- Support for MJML campaigns.
- Forms can now be configured to redirect users after successful submission and when double opt-in is required.
- Forms can now be configured with a custom message when double opt-in is required
- New email scheduler and rate limiter with significantly improved performance. (thanks @dompie for supporting the development of this feature)
- Show correct state while campaign is being prepared for sending on stats page
- Marking custom checkboxes as required actually requires users to check them now. Fixes #328. (thanks @cyberwuulf for reporting)
- Increased database timeout when inserting recipients to 60 seconds
- The analytics UI now shows a maximum of 20 links to avoid performance issues with large campaigns and individualized links
- Enabled translation of system strings on public forms.
- Campaigns with type
block
andjson_body
can now be created via the API. Implements #300 (thanks @dompie for suggesting)
- Image blocks now allow the use of Liquid in
src
and other attributes. Fixes #297 (thanks @dompie for reporting)
- Separators are now rendered more faithfully in the block editor
- Added PATCH and POST API endpoints for updating just the data field of a contact
- Liquid tags are now correctly rendered in layout blocks in the Block Editor (thanks @dompie for reporting)
- Fixed bug that caused "starts with" and "ends with" to be inverted in segment editor (thanks @dompie for reporting)
- Creating and updating contacts via the API now allows setting the
status
field. - Contacts list is now sorted in decending order by default
- Segments are now guaranteed to be initialized with a valid (empty) filter,
avoiding potential crashes with
nil
filters. MAILER_SMTP_FROM_EMAIL
is now used for sending system emails again.
- Avoid potential partial merging of system sender config with user-configured senders
- Only support TLSv1.2 for STARTTLS SMTP Senders to avoid issues with non-compliant TLSv1.3 implementation in OTP
- Added new Gmail user agent to avoid tracking invalid clicks/opens
- Return to list of unsubscribed/unreachable contacts after delete action from one of those pages. Implements #193 (thanks @digitalfredy for reporting)
- It's now possible to choose between the US and EU API endpoints for Mailgun senders (thanks @harryfear for reporting)
- Improvements to German translation (thanks @dompie)
- Clicking "Delete all" on the contacts list no longer causes a server error when no contacts have been selected. Fixes #260 (thanks @CSDUMMI for reporting)
- Fixed connection errors when using SMTP senders with STARTTLS (thanks @beep and @CodeOfTim for reporting)
Custom signup form fields + contacts search 🔎
- Custom fields (text, checkbox, dropdown, tags, numbers) can now be added to contact signup forms. Implements #135
- Search and sorting on contacts page
- Added buttons for inserting images, links, and buttons to plain Markdown editor. Fixes #255 (thanks @lukaprincic for suggesting)
- Errors in signup forms are displayed with a prominent red border and bold text now.
- If the
status
column is present in a CSV import, only rows where this column is set to "active" are imported. Fixes #253 (thanks @VZsI for reporting) - Live preview in plain Markdown editor no longer disappears when switching to rich editor and back.
- Fixed error when saving a contact with JSON data and a constraint error
- API Contact response now includes status field
- Fixed error when using custom email template for double opt-in emails
Double Opt-In ✅
- Added support for double opt-in/confirmed opt in. Forms can now be configured to enable double opt-in to require new subscribers to confirm their email address before they are added as contacts to the project.
- Updated Elixir to 1.15
- Translatable labels for first name and last name in form builder
- Refactored Form controller to separate config UI from public routes
- Refactored how form submissions are processed
- Refactored how Markdown campaigns are built
- Added support for additional JPEG variant (this avoids errors when uploading previously unrecognized JPEG files)
- Added
MAILER_ENABLE_STARTTLS
option to configure a system mailer with STARTTLS (#247)
- API DELETE endpoints for campaigns, contacts, segments now return the correct 204 response
- Added
MAILER_ENABLE_SSL
option to configure a system mailer with SSL/TLS
- Markdown campaigns now allow adding links to images (#245)
- Fixed SSL/TLS errors when sending emails with SMTP senders
- Fixed potential exception for image blocks without captions/urls
- CSV download for contacts and segments (#238 - thanks @katafrakt)
- Improved configuration form for SMTP senders with automatic port selection depending on security mode
- Fixed crash when using data URLs in campaigns (#218 - thanks @katafrakt)
- Support for Captchas from Friendly Captcha (thanks @beeb)
- Segments are now ordered alphabetically (#203 - thanks @panoramix360)
- Failed campaign emails are now logged via
Logger.warning/1
instead ofLogger.debug/1
.
- Email preview text is now actually included in emails
- Layout blocks now have a default 1:1 ratio, ensuring they are always rendered correctly
New Campaign Block Editor 📝
- New block-style editor for building campaigns
- Multi-column layouts
- Headings, paragraphs, lists
- Images
- Buttons
- Blockquotes
- Spacers
- More styling options in the form editor (#185)
- Changed base email template to Cerberus Hybrid
- Scheduled campaigns that could not be delivered are automatically un-scheduled
- Scrolling position in template and campaign previews is retained when changes are made
- Invalid signature markup no longer breaks the template editor (#119)
- Fixed error that caused uploaded images not to show up in upload modal (#186)
- Fixed some broken URLs when Keila is configured with
URL_PATH
(#189) - Mailer port now defaults to
587
instead ofnil
(#182 - thanks @aej) - Fixed bug that could cause the content of a Markdown campaign to be erased when changing the title (#188)
- Fixed bug that could cause the segment page to crash (#177)
- Only hard bounces are shown as bounces on campaign statistics page
- Legacy IDs are now decoded correctly
Better Campaign Analytics 📈
- New Campaign Analytics page
- Improved Contact Activity Stream
- Separate lists for active/unsubscribed/unreachable contacts
- Improved handling and logging of campaign delivery errors
- Bot detection in campaign open tracking (#164 - thanks @panoramix360)
- New API endpoint to list Senders (#147)
- Added support for Postmark senders (thanks @aej)
- Improved compatibility with SMTP servers by relaxing
gen_smtp
SSL/TLS settings - Upgraded to Elixir 1.14
- Ugraded to Tailwind 3
- Added success hint when copying API key to clipboard
- Fixed error when no user content dir is set (#171)
- Fixed error when CSRF is enabled for forms (#167)
- Breaking: Hashids now use configurable salt. Read more on keila.io
Image Uploads, Rate Limits, Do-not-track Campaigns 🖼️
- Image Uploads
- Do-not-track option for campaigns
- Liquid templating support in campaign subjects
- First interface translation added: German
- Rate limits for Senders and Shared Senders (thanks @gbottari & @panoramix360!)
- Allow configuring
FROM
email address for system emails - Shared local sender for testing/development
- Improved styling: New spacer block and additional styling for signatures
- Configurable FROM email address for system emails
- Updated LiveView to 0.17
- Images in the WYSIWYG editor can now be modified on double-click
- Easier configuration of public-facing Keila URLs
- API keys don’t expire anymore
- Fixed some typos (thanks @kianmeng!)
- Liquid tags can be used as link targets now
Campaign Data, Improved WYSIWYG editor 💽
- Campaign data feature
- Automatic recognition of Markdown in WYSIWYG editor
- New Code button in WYSIWYG editor with Liquid template examples
- Highlighting of Liquid tags in WYSIWYG editor
- Pretty-printing of embedded form HTML
- Support for Dev Containers
- Updated JS dependencies
- Added size constraint to contact data field (8 KB per contact)
- Simplified UI for campaign editor with settings moved to modal
- Link click statistics are now displayed live while campaign is sending
- Use full URL for embedded form action attribute
API for Contacts & Campaigns, Better Imports, UX Improvements 🧑💻
- API for managing Contacts, Campaigns, Segments
- Swagger UI for API at
/api
- Improved contact import with support for custom data and upserts
- Notifications when leaving pages with unsaved data
- Added
DB_ENABLE_SSL
configuration option
- Updated Oban and Jason dependencies
- Segments with custom data fields can now be edited after saving
- Error display in campaigns without sender no longer keeps reloading page
- Fixed error when creating new segments
- Contact segmentation
- Support for custom contact data
- Allow deletion of sent campaigns
- Improved UI design
- Moved all templates from leex/eex to heex
- Improved Core querying API
- Fixed exception when processing unhandled SES webhooks
- Configuration option to run Keila in a subdirectory
- Login-as feature for admins
- Gzip compression of assets
- Upgraded to Phoenix 1.6
- Upgraded various dependencies, including Ecto
- Replaced Webpack with esbuild
- Default contact status is now subscribed
Contact Activity Log & Bounce Handling 🗒️
- Upon unsubscribing, contacts are no longer deleted from the database
- Contact activity log
- Contact dashboard with subscriber numbers
- Support for Configuration Sets for AWS SES
- Automatic handling of bounces and complaints for AWS SES
- Removed password placeholder texts
- Dockerfile and sample docker-compose configuration are now compatible
- Template now fully compatible with Outlook and Windows Mail
- Fixed broken CSV template downloads
- Improved template display in WYSIWG editor
- Fixed broken styling on non-authenticated routes
- Improved onboarding experience with empty states for all views
- Improved dark app design
- Stricter code-checks in CI
- Default template is now displayed correctly in campaign editor
- Paddle webhooks now have improved idempotency
- Added click/open tracking for campaign emails
Precedence: Bulk
header now included in all campaign emails- Implemented per-instance
SharedSenders
- Implemented Shared Senders for AWS SES
- Added account and account credits for organizing users and implementing quotas
- Added subscription plans for app.keila.io
- Updated to Elixir 1.12
- Removed email preview text from Cerberus
Template customization & UI improvements
- Template editor for customizing Markdown campaign styles
- Improved index pages for forms, templates, and campaigns
- Updated dependencies, using upstream of
Swoosh
again
- Fixed broken template download links in production
Scheduling campaigns & WYSIWYG editor ⏲️
- Campaigns can now be scheduled to be sent automatically
- WYSIWYG editor for Markdown campaigns
- Local sender for testing in development mode
- Formatted dates in local timezone now used on campaign overview page
- Removed default email preview text
- Fixed crash when starting release
- TailwindCSS styles are now pruned, massively reducing CSS size
- Default admin user is created correctly when
KEILA_USER
is not specified - Fixed crash when starting release
Simplified deployments ⚙️
- Improved deployment workflow with automatic migrations
- Automatic creation of root user
- Admin panel with simple user management
- Campaigns can no longer be sent twice
First official release of Keila 🚀
This first release implements the most important features to make Keila a viable tool for managing newsletters.
- Editor for plain text + Markdown campaigns
- Sending campaigns with SMTP, SES, Sendgrid, Mailgun
- Signup forms and form editor
- Contact import
- One-click unsubscription