Release announcements, helpful tips, and community discussion


Cerb (10.4) is a feature upgrade released on August 04, 2023. It includes more than 101 new features and improvements from community feedback.

To check if you qualify for this release as a free update, view Setup » Configure » License. If your software updates expire on or after December 31, 2022 then you can upgrade without renewing your license. Cerb Cloud subscribers will be upgraded automatically.

Important Release Notes



  • [Profiles/Widgets] On profile tabs, widgets may now be shown or hidden using conditional logic. For instance, a widget may only be visible when tickets are within a certain group, or lack a custom field value. When at least one widget is hidden, admins will see a ‘Toggle Hidden’ button at the top of a profile tab. This allows hidden widgets to still be rendered or edited.

  • [Sheets] Sheet KATA may define optional column parameters for color: and text_color:. When used with the @raw annotation, the value may contain scripting and row placeholders. The value should be the key of a color set defined in layout:colors: as a list of HEX color codes (e.g. #ffcc00). The value may contain a :n suffix, where n is the 0-based index from the color set. This can be done dynamically for threshold colors. By default, the first color is used. If a color set contains a _dark suffix, it will be used automatically in dark mode (e.g. rainbow12 will check for the existence of rainbow12_dark). Entire rows can be colorized by using the same color: parameter on all columns. [#1111]

  • [Sheets] Sheet KATA may define an optional column parameter for text_size:. This is a numeric percentage of the current text size (e.g. text_size: 150). This is particularly useful with layout:style: fieldsets when rendering cards.

  • [Custom Fields/Markdown] ‘Text: Multiple Lines’ custom fields may now be configured to allow Markdown formatting. Markdown formatting options will be displayed in the record editor, along with a rendering preview. The |markdown_to_html filter can be used in sheets to show the formatting. [#1085]

  • [Sheets/Markdown] Added a new markdown column type to sheets. This displays Markdown formatted text with sanitized HTML output and the default stylesheet. This pairs well with the new feature to enable a Markdown editor on ‘Text: Multiple Lines’ custom fields.

  • [Explore/Automations] Explore mode can now be backed by interaction automations (interaction.worker.explore) to navigate through dynamic sets of URLs.

    Currently, explore mode uses a static point-in-time snapshot of a worklist for the first 1,000 records. This ignores new higher priority records that entered a worklist after explore started, and it includes records that may have been handled recently by other workers.

    With dynamic explore mode, a ‘next’ button can determine the next most important task in real-time (including new records and excluding records that no longer match filters).

    The interaction is expected to return await:explore: with a title, url, and optional label for the next URL to visit. The interaction receives inputs for explore_hash:, explore_page: and the current worker.

    A custom conditional toolbar can be displayed with each URL. The toolbar interactions can be interactive or headless, and they can return an explore_page: for navigating the explore set (e.g. back/next).

    For convenience, named toolbar buttons (e.g. interaction/next:) can also be defined without an automation uri:, where the name will be treated as the explore_page: action.

    Custom keyboard shortcuts can be assigned to each toolbar button. This will significantly optimize workflows that involve visiting a set of records/URLs (e.g. dispatch, finding a next assignment, etc).

    Explore sets are created with api.command: in automations.

  • [Explore/Automations] Added reference automations for custom explore mode. See cerb.worklist.buttons.explore and cerb.explore.worklist. Call the former from the records.worklist toolbar.

  • [Toolbars/Worklists] Added a customizable records.worklist toolbar that displays beneath every worklist in the UI. Toolbar items may be added based on the worklist ID, record type, query, or current worker. Interactions receive caller parameters for worklist_id, worklist_record_type, and selected_record_ids. An after:refresh_worklist@bool: option determines if the worklist refreshes after the interaction (default yes).

  • [Automations] Added the api.command: instruction to automations. This interacts with APIs to perform various actions that were previously impossible from automation code (e.g. creating explorer sets, pre-signing AWS URLs, reading worker preferences, validating OAuth2 tokens). Each API command has a name: and an optional set of params:. New API commands can be added using the plugin system.

  • [Automations/Events] Added a new mail.moved automation event. This triggers every time a ticket is moved between groups or buckets, including cascading routing like (A->B->C). The event includes placeholders for ticket_ as well as was_group_ and was_bucket_. This simplifies conditional routing that depends on where a ticket was moved from.

  • [Developers/Docker/Compose] Added a Docker Compose configuration to ./install/docker/. This dramatically simplifies development, testing, and evaluation by spinning up local pre-configured containers for Nginx, PHP-FPM, and MySQL with a single docker compose up command. The directory also includes an annotated custom-example to manage multiple environments. Changes to the local Cerb files are instantly reflected in the deployed containers. These files should not be used in production, but can be used as a reference for doing so with Docker.

    See: Launch Cerb in Docker

  • [Automations/Visualization] In the automation editor popup, the ‘Visualization’ tab now shows a proper ‘directed acyclic graph’ (DAG) for the possible execution paths of the automation. The graph supports panning and zooming with the mouse. Clicking on a node automatically scrolls to the relevant line in the code editor.

  • [Automations] In automations, added the ability to use return@key: to return a dynamic dictionary. The value must be a colon (:) delimited key path. For instance, return@key: inputs.

  • [Automations/Editor] The automation editor now gives autocompletion suggestions for @annotations in KATA code. Multiple automations can be chained together with commas. [#1611]

  • [Chart KATA] When editing ‘Chart KATA’ widgets, the tick formatters for date: and number: patterns now have an interactive helper. Previously, this required manually entering formatting strings like %Y-%m-%d %H:%M:%S or .4f.

  • [Toolbars/Icons] In toolbars, interactions and menus with an icon: may now specify an optional icon_at: key with a value of start or end. This determines the placement of the icon relative to the label.

  • [Toolbars/Comments] Added a comment.editor toolbar to display interactions above the editor when writing comments. This receives caller parameters for record_id, record_type, and selected_text. For instance, a translation service can be used to translate selected text and paste it into the comment editor. Thanks to 1Password for the feature request!

  • [Interactions/Worker] Added a reusable cerb.ticket.status worker interaction for changing the status of a ticket. This is used on ticket profiles in the ‘Status’ widget. The inputs:record/ticket: and inputs:text/status: are required. An optional inputs:text/confirm: displays a confirmation message before proceeding (e.g. closing, deletion).

  • [Interactions/Worker] Added a reusable cerb.ticket.assign worker interaction for changing the owner of a ticket. This is used on ticket profiles in the ‘Owner’ widget.

  • [Cards/Sheets] On ‘Sheet’ card widgets, implemented the grid and columns layouts.

  • [Profiles/Sheets] On ‘Sheet’ profile widgets, implemented the grid and columns layouts.

  • [Workspaces/Sheets] On ‘Sheet’ workspace widgets, implemented the grid and columns layouts.

  • [Sheets/Interactions] In sheet widgets, added a new interaction: column for launching an arbitrary interaction from a link. The link text, interaction uri, and inputs are configurable for each row.

  • [Automations/Records] In automations, the record.get: command may now provide the record_expand: option for additional keys to expand. This makes it consistent with other commands like

  • [Profiles/Dashboards/UX] On profile tabs, admins have a new ‘Edit Tab’ shortcut button in the top toolbar. This saves several clicks when renaming a dashboard or changing its layout.

  • [Sheets/Widgets] On sheet widgets, added the ability to define default data: when a data source is empty. For instance, this is useful to display static data (e.g. announcements on a workspace) with the functionality of sheets (paging, formatting).

  • [Profiles/Sheets] On ‘Sheet’ profile widgets, custom keyboard: shortcuts can be configured for toolbar interactions.

  • [Cards/Toolbars] On record.card toolbars, added a new after:refresh_toolbar@bool: option to refresh the toolbar after an interaction finishes. For instance, if an interaction changes the status of a record and toolbar items are conditional on status. [#1726]

  • [Profiles/Toolbars] On record.profile toolbars, added a new after:refresh_toolbar@bool: option to refresh the toolbar after an interaction finishes. [#1726]

  • [Setup/Developers] In the Setup->Developers menu, added a new ‘Platform’ page. This allows developers to clear the server-side cache with a single click. It also provides the ability to reload/synchronize built-in assets from disk: automations, package library, and resources. [#1683]

  • [Records/Tickets] When updating ticket records, a new bucket: field may be used with a literal bucket name to match. This improves readability in automations. Previously, only a record ID could be used, like bucket_id: 123. When using the new field, either group: or group_id: must also be provided to disambiguate bucket names like ‘Inbox’.

  • [Sheets/Toolbars] Added a new toolbar: column type to sheets. This displays an interactions toolbar for each row. It’s particularly useful with layout:style: fieldsets to summarize a record with common actions. [#1467]

  • [Portals/Interactions] On third-party websites using an portal, DOM elements may include an data-cerb-interaction-style attribute to configure the interaction method. The possible values are float (default; interactions appear in a floating popup window in the bottom left of the browser), full (same as float but the popup uses the full browser width), and embed (the interaction is inline with page content).

  • [Cards/Toolbars] record.card toolbars may now include an after:refresh_toolbar@bool: action. This refreshes the toolbar at the top of the profile page in the same manner as after:refresh_widgets:.

  • [Profiles/Toolbars] record.profile toolbars may now include an after:refresh_toolbar@bool: action. This refreshes the toolbar at the top of the profile page in the same manner as after:refresh_widgets:.

  • [Widgets/Sheets] On sheet widgets (cards, profiles, workspaces), interactions launched from a sheet toolbar now include caller parameters for rows_selected and rows_visible. If a selection column is defined, these parameters respectively are set to the value of the currently selected sheet rows, and the value of all sheet rows displayed on the current page. This removes the need to explicitly pass selected sheet rows to an interaction for these widgets. As well, it makes it possible to act on not only selected sheet rows, but also deselected rows, or an entire page at once (e.g. bulk update). Thanks to @mryanb for the feature request!

  • [Automations/Scripting] In automation scripting, added an |html_to_text(truncate) filter for converting an HTML document to plaintext. This uses the same functionality that generates a plaintext part for inbound HTML messages.

  • [Custom Records/Subtotals] In custom record types, implemented the ability to subtotal by record name.

  • [Worklists/Export] When exporting a worklist in the JSON/JSONL formats, a column value: may now be a nested object rather than just a string. This change simplifies exporting data for integrations (e.g. HuggingFace model training).

  • [Interactions/Automations] In interaction.worker automations, added a return:open_url: key to open a URL in the current browser window. The existing return:open_link: key opens the URL in a new tab.

  • [Automations] In automations, added cerb.commands.oauth2.token.validate to the api.command:. This validates an OAuth2 token generated by Cerb; allowing tokens to be used in integrations like a custom API from webhooks. An error is triggered if the token is invalid. On valid tokens, the worker and OAuth app associated with the token are returned.

  • [Automations] In automations, added to the api.command:. This returns the personalized worklist configuration and search results for a given worklist and worker. For instance, the command can fetch the next ticket from a team worklist in priority order for each worker. This is particularly useful when implementing a custom ‘explore mode’ on worklists.

  • [Automations] In automations, added cerb.commands.worklist.explorer.create to api.command:. This creates a dynamic explore set using an interaction.worker.explore automation to generate the next URL to visit. For instance, where the built-in explore mode uses a point-in-time snapshot of worklist records, a dynamic explore set can include or exclude records in a worklist based on changes after explore mode started (e.g. recently replied/closed tickets). The command returns the unique identifier (hash) of the new explore set.

  • [Toolbars] In Toolbar KATA, a new class: option allows CSS classes to be added to toolbar items. This allows functionality that renders a toolbar to provide extra functionality. For instance, hiding toolbar buttons unless at least one sheet row is selected.


  • [Platform/PHP] Cerb now requires PHP 8.1 or later.

  • [Logins/UX] When a worker attempts to log in from an expired login form, a more helpful error message is now displayed with a link to log back in. For instance, this would occur if a worker logged out and then left the browser tab open overnight on the login form. Cerb maintenance would remove the unauthenticated session in the meantime, invalidating the form’s CSRF token. Previously the error was a blank white page that confusingly said, “Access denied”. [#1375]

  • [Sessions/UX] When a worker session expires but the browser remains open, the UI now responds more gracefully on links/buttons that don’t trigger a full page reload (e.g. popups, interactions). These actions now display an alert prompting a worker to log back in, without navigating away from the current page. Record editors and reply forms interrupt the save button rather than silently discarding unsaved changes.

  • [Interactions/Workers] In interaction.worker automations, added autocomplete suggestions for the hidden@bool: option on all await:form: elements. This allows form elements to be rendered conditionally rather than having to build a list of elements using outcome:.

  • [Records/Automations] Renamed the record.profile.viewed automation event to record.viewed. The event now triggers for both card popups and profile pages, distinguishable by the is_card input. Thanks to mryanb for the request!

  • [Records/Custom Fields/UX] In record editors, typing backspace in an empty list-based custom field value removes the text input.

  • [Profiles/Ticket] On ticket profiles, the ‘Actions’ custom HTML widget is removed if unmodified. Otherwise, it is prefixed with ‘(DEPRECATED)’. Custom HTML widgets should be migrated to interaction toolbar widgets. The actions are all now available elsewhere on the profile page.

  • [Profiles/Tickets/Conversation] On ticket profile pages, the ‘Read All’ button is now built-in to the ‘Conversation’ widget. Previously this was implemented in the ‘Actions’ custom HTML widget. The ‘A’ shortcut continues to function as before.

  • [Records/Profiles/Merge] On record profile pages, the ‘Merge’ button has moved to the top toolbar (if a worker has permission). Previously this was implemented in an ‘Actions’ custom HTML widget.

  • [Records/Search/Performance] When searching records, full-text query terms with special characters (e.g. email addresses, URLs, hosts, IPs, phone numbers) are now automatically quoted for phrase matching. This improves match relevance, and also improves performance by using fewer results in joins.

  • [Profiles/Widgets/Sheets] On ticket and task profiles, the ‘Status’ widget now uses a fieldsets-based sheet rather than the deprecated ‘HTML/Javascript’ type. These sheets are much easier to customize with KATA (e.g. card popups, keyboard shortcuts, colors, font sizes).

  • [Profiles/Widgets/Sheets] On ticket profiles, the ‘Owner’ widget now uses a fieldsets-based sheet rather than the deprecated ‘HTML/Javascript’ type. These sheets are much easier to customize with KATA (e.g. card popups, keyboard shortcuts, colors, font sizes).

  • [Automations/Scripting] In automation scripting, the is pattern test now allows a mix of strings and string arrays as parameters. This makes it much easier to use a dynamically generated list of rules (e.g. {{sender_address is pattern (rules)}}).

  • [Sheets/Widgets] When configuring card, profile, and workspace Sheet widgets, the sheet editor now provides an ‘Insert Placeholder’ menu.

  • [Toolbars/UX] When editing Toolbar KATA, the after: option now offers extension-specific autocompletion suggestions.

  • [Automations/Events/Editor] The automation event editor now properly suggests uri: auto-completions only for the current event. Previously, this suggested any matching automation.

  • [Code Editors/Performance] Added a “debounce” to code editor autocompletion so live suggestions are only updated after a 250ms pause in typing. Previously, requests were sent to the server on every keystroke.

  • [Records/Performance] Improved performance when searching message records. The results now only return record IDs which can often be served entirely by in-memory indexes. The record fields are loaded per page of results, rather than for the entire set.

  • [Worklists/Search/UX] In worklists, search queries with syntax errors now clearly display a warning above the list.

  • [Mail/Formatting] When composing or replying to mail, the formatting toolbar buttons for ‘Unordered List’ and ‘Quote’ now insert formatting even when no text is selected. Previously they only worked with an editor text selection.

  • [Behaviors/Widgets] In bot behaviors and widgets, the ‘Insert Placeholder’ menu now inserts custom field placeholders using their descriptive URI rather than the legacy custom_123 syntax.

  • [Performance/Custom Fieldsets] Improved the performance of some database queries involving custom fieldsets.

  • [Cards/Widgets/Sheets] On ‘Sheet’ card widgets, toolbar KATA now offers autocompletion suggestions for interaction:after:.

  • [Profiles/Widgets/Sheets] On ‘Sheet’ profile widgets, toolbar KATA now offers autocompletion suggestions for interaction:after:.

  • [Workspaces/Widgets/Sheets] On ‘Sheet’ workspace widgets, toolbar KATA now offers autocompletion suggestions for interaction:after:.

  • [Interactions/Tickets] Improved the built-in cerb.ticket.move interaction used on ticket profiles. Groups and buckets can now be selected with a single-click and no extra ‘Continue’ step.

  • [Installer] The installer will now attempt to initialize an empty ./storage directory. This simplifies deployment in containers, continuous integration, and testing.

  • [Records/Fulltext/Performance] When using the default MySQL Fulltext search engine, now only the first 50,000 characters are indexed.

  • [Platform/Sessions] Sessions no longer set or read cookies on requests to /webhooks, /debug or /update.

  • [Bots/Behaviors] When creating comments in a legacy bot behavior, only workers that are @mentioned will be notified. Previously workers could also be selected from a list.

  • [Comments/Notifications] Creating comment records from automations or the API now properly triggers @mention notifications. Previously notifications were only sent for comments created from the web UI.

  • [Sheets/UX] On sheets with a card column, middle click or cmd+click will now open links in a new tab if their dictionary contains a record_url key. The built-in record type dictionaries include this automatically.

  • [Platform/Dependencies] Updated the Ace Editor dependency from v1.4.14 to v1.12.5. This is used in code editors and the changeset history diff viewer.

  • [Platform/Dependencies] Upgraded the jQuery library from 3.6.0 to 3.6.4.

  • [Platform/Dependencies] Updated singpolyma/openpgp-php from 0.5.0 to 0.6.0. This is the library used for sending and receiving PGP encrypted email messages.

  • [Platform/Dependencies] Updated tijsverkoyen/css-to-inline-styles from 2.2.4 to 2.2.6. This resolves an issue with 4-byte emoji in email and comments. It also improves PHP 8.2 support.

  • [Platform/Dependencies] Updated phpseclib from ~2.0 to 3.0.18.

  • [Platform/Dependencies/Twig] Updated the Twig template engine from 3.4.3 to 3.6.2.

  • [Platform/Dependencies] Updated the Horde IMAP library from 2.30.4 to 2.30.6.


  • [Portals/Support Center] Removed jQuery from the legacy Support Center portal. It now uses vanilla Javascript.

  • [Installer] Removed jQuery from the guided installer. It now uses vanilla Javascript.


  • [Mail/Relay] Fixed an issue with bot behaviors using the ‘Relay Email to Workers’ action. If a group didn’t have a reply-to address assigned then a fatal error could prematurely abort email parsing.

  • [Records/Search] Fixed an issue when full-text searching records where a term like a*star could perform an inadvertent wildcard match for all terms beginning with ‘a’. This is now treated as a literal phrase match.

  • [Records/UX] Fixed an issue on record editor popups where an expired session could misleadingly still report ‘Saved!’ when the ‘Save Changes’ button was clicked. This now shows an obvious error.

  • [Platform/Sessions] Fixed an issue where the initial session cookie wasn’t set with samesite=lax. The flag was only set when the cookie was refreshed 2.5 minutes later.

  • [Records/Custom Fields/UX] In record editors, after adding a new value to a list-based custom field, focus is now properly set on the new text input to save the extra click.

  • [Portals/Support Center] Fixed an issue in the Support Center portal where reloading the page after creating a new ticket would duplicate it.

  • [Portals/Support Center] Fixed an issue in the legacy Support Center portal where a user could duplicate a reply in the ticket history if they reloaded the browser page immediately afterward.

  • [Interactions/Toolbars] Fixed an issue with toolbars when interactions contained a nested input key.

  • [Mail/Parser] Fixed a fatal error in the email parser if a message file was removed from ./storage/mail/ while still processing.

  • [Mail/Parser] Fixed a fatal error when downloading messages from a POP3/IMAP mailbox and the server hangs up unexpectedly.

  • [Sessions/Headers] Added better handling to a rare situation where HTTP headers may be sent before the session started. Previously this displayed a blank white screen and an error log entry. Now a more helpful error is displayed in the browser.

  • [Interactions/Websites] Fixed an issue on automations where pressing enter in a text input broke the form. This now properly advances the interaction.

  • [Interactions/Website] Editing an automation now properly suggestions label: autocompletion for await:form: elements.

  • [Interactions/Website] Fixed an issue with automations where a long await:form: could scroll down to the first focusable element below the page fold. This required a user to scroll back up to read the full message.

  • [Interactions/Website] Fixed several UI/style issues with automations. The browser button outline was hidden, making it difficult to tab through forms. Added a hover effect to form buttons. Enforced an underline style on links (the underlying website could remove the underline).


  • [Security/Cookies] Logins now always use a more secure temporary ‘session’ cookie rather than a permanent cookie with a future expiration. The cookie expiration is no longer extended every 2 minutes, since this caused various issues (primarily for Safari users). Idle sessions are still deleted on the server based on the inactivity threshold configured in Setup » Security. Several new inactivity presets have been added, and the ‘When the browser is closed’ has been removed (the server session was never deleted when the browser cookie was, it misleadingly expired after 24h). Using an explicit inactivity threshold (e.g. “4 hours”) is a better security practice.

  • [Security/Sessions] The session cookie now sets an explicit path (e.g. /helpdesk/). Previously cookies were always set on the top-level domain. This could cause problems with multiple Cerb instances hosted on the same domain in different paths.

  • [Security/CSP] Added the APP_SECURITY_CSP_MEDIA_SRC option to the framework.config.php file to override the default media-src Content Security Policy (CSP).