- 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