⚠️ Breaking changes
- The GraphQL metadata API no longer uses nestjs-query cursor types. Remove ObjectConnection, FieldConnection, CursorPaging, and related cursor inputs from metadata queries and switch to the new resolver-based shape. (#23925) by @abdulrahmancodes
- Server image security update: undici bumped from v6/v7 to v8 (potentially breaking for direct callers), plus js-yaml, sharp, brace-expansion, ip-address, and dompurify patched for critical and high CVEs. Self-hosters should update. (#23963) by @charlesBochet
🌟 Highlights
- Hidden records no longer surface as chart labels through unfiltered relation joins. (#23369)
- A new /chat mode keeps the conversation front and center with records opening as side-panel artifacts. (#23929)
- Campaign email bodies now have a canvas editor with sections, columns, buttons, and image blocks. (#23657)
- Views can now be displayed as a List, alongside table, board, and calendar. (#23829)
- Records that no longer match an active filter disappear from the open table in real time. (#23858)
- Opening and closing a currency field editor without typing no longer corrupts the stored amount. (#23874)
✨ New features
- The runAgent API now accepts a messages array for multi-turn bot conversations alongside the existing single-prompt form. (#23395) by @abdulrahmancodes
- Apps can now run an agent on behalf of a specific workspace member, scoping the run to that person's permissions. (#23470) by @abdulrahmancodes
- Large-company signups now see an optional Cal.com call-booking step during onboarding, between the team invite and plan selection. (#23521) by @bosiraphael
- The Slack bot now includes direct links to records it creates or surfaces, so you can open them from the reply. (#23539) by @abdulrahmancodes
- Fireflies calls missed by webhooks can be backfilled via POST /fireflies/backfill; the work fans out as batched background jobs. (#23622) by @ehconitin
- Campaign email bodies now open in a canvas editor with sections, columns, buttons, dividers, and image blocks that render to email-safe HTML per recipient. (#23657) by @ijreilly
- Navigating away from the workspace setup chat now carries the conversation into the Ask AI side panel instead of losing it. (#23744) by @bosiraphael
- The transcript provider for call recordings is now a workspace-admin setting. The default is Recall.ai; Gladia remains available for mixed-language calls. (#23789) by @ehconitin
- Record pages can now show a field widget listing records two relation hops away — e.g. a Client page listing Transactions of the Client's Wallets. (#23815) by @FelixMalfait
- A sending domain waiting on DKIM record propagation now shows as Pending instead of Failed in email domain settings. (#23818) by @ijreilly
- Views can now be displayed as a List, alongside the existing Table, Board, and Calendar layouts. (#23829) by @neo773
- The Slack bot now answers empty mentions and DMs with example prompts, and notifies lapsed threads rather than dropping them silently. (#23835) by @abdulrahmancodes
- The bot's own mention is stripped wherever it appears in a Slack message, not just at the very start. (#23837) by @abdulrahmancodes
- The Slack bot now uses Slack's native agent surface, gaining a built-in thinking indicator, suggested prompts, and thread titles. (#23838) by @abdulrahmancodes
- A new /chat mode keeps the AI conversation in the main pane; records and views the agent surfaces open as side-panel artifacts next to the thread. (#23929) by @FelixMalfait
- List-layout views now render as a proper list widget instead of silently falling through to a table renderer. (#23940) by @FelixMalfait
- Recipient chips in the email composer can be dragged to reorder, moved across To/Cc/Bcc, range-selected with shift or cmd/ctrl, and deleted as a group. (#23947) by @FelixMalfait
- The email composer header is restyled with flush rows, inline labels, and a clean divider — closer to a mail client than a settings form. (#23948) by @FelixMalfait
💅 Improvements & UX
- A failed page chunk load no longer shows a raw browser error string in a snackbar. (#23571) by @bosiraphael
- Microsoft Graph webhook lifecycle events now route correctly: subscriptionRemoved recreates the subscription, reauthorizationRequired renews it in place. (#23707) by @neo773
- Onboarding no longer shows free-credit tags or reward prompts when billing is disabled on the instance. (#23717) by @bosiraphael
- The Slack bot no longer calls the Slack API to identify itself on every channel-join event; the bot user id is cached at connect time. (#23726) by @abdulrahmancodes
- Expired invitations are no longer treated as active and no longer count toward the onboarding invitation limit. (#23749) by @prastoin
- Drag-and-drop across the app now uses a single consistent drop-position algorithm; table rows, Kanban cards, and tabs drop where you aim them. (#23752) by @git-init-priyanshu
- Disconnecting a Slack workspace now releases the team claim so another Twenty workspace can connect to the same Slack team. (#23753) by @abdulrahmancodes
- A new cross-workspace chat list in the admin panel (AI tab) shows all AI conversations, filterable by onboarding status, errors, and workspace. (#23757) by @bosiraphael
- Dragged table rows and Kanban cards now share a consistent background and opacity treatment. (#23772) by @Bonapara
- During workspace setup, only display name, subdomain, and logo can be changed; all other fields are locked until the workspace is activated. (#23781) by @prastoin
- A stray ] that sometimes appeared after AI chat record chips is fixed. (#23798) by @bosiraphael
- Recall call recordings that end due to bot-detection timeout are now processed normally instead of being marked as not recorded. (#23812) by @ehconitin
- Streaming AI chat messages now render at constant cost per chunk instead of slowing down as the message grows. (#23831) by @FelixMalfait
- The button that opens the credit package picker is renamed from 'Increase' to 'Manage'. (#23840) by @FelixMalfait
- The active session list and 'Log out all devices' in Settings → Devices now apply only to the current workspace. (#23843) by @FelixMalfait
- The bottom border of the last row in a record table is no longer missing. (#23846) by @FelixMalfait
- Opening the Ask AI panel with a fast-model preselection no longer silently falls back to the smart model. (#23854) by @charlesBochet
- Opening Monaco-backed editors (code logic, MCP settings, workflow code) no longer causes worker method errors from having two Monaco instances loaded. (#23855) by @FelixMalfait
- Fonts are now served from Twenty's own servers; no browsing data is shared with Google Fonts when loading any Twenty page. (#23859) by @FelixMalfait
- JSON-LD structured data in page heads is now properly escaped, closing a potential XSS path from crafted string values in page titles or names. (#23865) by @FelixMalfait
- The HTML block preview sanitizer now removes template, slot, and portal elements to close a parser-differential gap. (#23866) by @FelixMalfait
- Workspace subdomains can now be as short as one character (previously the minimum was three). (#23871) by @ijreilly
- Invitees who complete onboarding are no longer routed to a dead-end workspace setup page. (#23881) by @bosiraphael
- On mobile, the record page header shows only the record title; the object type and pagination count are hidden where there is not enough room. (#23902) by @FelixMalfait
- Webhook subscriptions backed by an invalid OAuth refresh token are now expired rather than retried indefinitely. (#23907) by @neo773
- Command menu item labels can now be translated. (#23913) by @Dumeng
- Lab feature flags must now declare an icon server-side; the List View toggle now shows the correct icon in Settings → Lab. (#23930) by @FelixMalfait
🐛 Bug fixes
- Hidden records no longer appear as chart labels — charts grouped by a relation field now filter the join and show only records the viewer is allowed to see. (#23369) by @bosiraphael
- A view grouped by a field with only one groupable option (e.g. People grouped by Company) can now have its grouping changed or removed. (#23619) by @abdulrahmancodes
- App-manifest RECORD_TABLE widgets can now reference a view by viewUniversalIdentifier, matching the portability that FIELDS widgets already had. (#23634) by @thomtrp
- HTML and HTM file previews now expand to fill the preview modal instead of rendering at the default inline iframe size. (#23676) by @Shinu-Cherian
- Advanced filters on relation fields (e.g. Assignee 'Is Me') now show a readable chip instead of raw JSON. (#23718) by @charlesBochet
- Settings → AI → Tools no longer shows a misleading 'App version mismatch' error. (#23731) by @FelixMalfait
- Passing a single object (not an array) to a GraphQL or filter no longer throws a runtime error. (#23738) by @bosiraphael
- The 'Discard Draft' workflow action is now hidden when the draft is the workflow's only version. (#23756) by @thomtrp
- An If/Else workflow step with a filterGroupId that resolves to nothing now correctly rejects the branch instead of matching unconditionally. (#23758) by @blockgroot
- If the install-apps onboarding job fails to enqueue, the step is restored so users can retry instead of being silently skipped past it. (#23759) by @bosiraphael
- fix(twenty-front): stop field widget layout dropdown from crashing th… (#23784) by @FelixMalfait
- Draft and send email agent actions now use the current user's connected account rather than whichever account the database returns first. (#23793) by @charlesBochet
- Logic function dependency layers that pull in large packages no longer crash the build Lambda with out-of-memory errors. (#23805) by @charlesBochet
- The Body editor in the Send Email workflow action panel no longer overlaps the Attachments section on short panels. (#23810) by @ijreilly
- AI-generated email campaigns can now be saved from queue workers; the workspace auth context is set correctly outside HTTP requests. (#23811) by @FelixMalfait
- Relation-traversal filters on record table widget views are no longer silently dropped when the layout is saved or filters are edited in the side panel. (#23814) by @FelixMalfait
- Starting a drag on a row that unmounts before the gesture activates no longer crashes with 'Cannot start a drag operation without a drag source'. (#23816) by @FelixMalfait
- Editing an advanced filter rule on a freshly loaded page — changing the operand or toggling a relation value — now actually applies. (#23819) by @charlesBochet
- Creating a record from a view with a nested-relation filter no longer fails with 'Unexpected value for UUID filter'. (#23832) by @FelixMalfait
- A record that no longer matches the active filter (e.g. a task marked Done in an Open-status view) now disappears from the open table without a manual reload. (#23858) by @charlesBochet
- Clearing a to-one relation field in a Create Record or Upsert Record workflow step now removes the value; previously it stored {id: null} and failed at runtime. (#23869) by @thomtrp
- Opening and closing a currency field editor without typing no longer corrupts the stored amount. (#23874) by @thomtrp
- Adding, editing, or deleting an item in a multi-value field (e.g. phone numbers, links) and pressing Enter now saves the change immediately. (#23943) by @FelixMalfait
- A record page whose title field holds a non-string value no longer crashes the app with 'trim is not a function'. (#23950) by @thomtrp
- When the AI agent's find_many tool returns a truncated page, it now tells the model results were cut off, so the model no longer reports records as missing. (#23955) by @thomtrp
⚡ Performance
- Event-loop delay (mean, p50, p99, max) and workspace cache recompute times are now exported to Prometheus. (#23751) by @charlesBochet
- Per-pod workspace metadata cache occupancy — entry count, size, distinct workspaces, and per-provider bytes — is now tracked in Prometheus, with heavy providers capped by entry count. (#23778) by @charlesBochet
- AWS SDK clients now have an explicit request timeout; a stuck request can no longer exhaust the connection pool and hang the process. (#23857) by @charlesBochet
- V8 GC pause duration and heap metrics are now exported to Prometheus to help attribute high-p99 latency stalls to garbage collection. (#23879) by @charlesBochet
- A new IS_WORKSPACE_CACHE_COMPACT_STORAGE_ENABLED flag enables compacted cache payloads that reduce GC pressure on large deployments. (#23880) by @charlesBochet
- The workspace cache now tracks entry count per provider in Prometheus, making it possible to attribute memory growth to specific providers. (#23883) by @charlesBochet
🔧 Developer & API
- A new server upgrade command repairs orphan workflowVersion rows in the core schema before dispatch-from-core is enabled on a workspace. (#23739) by @thomtrp
- A new IS_FEATURE_FLAG_MANAGEMENT_ENABLED environment variable enables the feature flag admin tab on self-hosted and preview instances. (#23750) by @FelixMalfait
- The Vite dev server now proxies API routes so local development runs same-origin without CORS errors when cookie auth is enabled. (#23779) by @prastoin
- Workspace migration build and run timings are now exported as OpenTelemetry histograms via MetricsService. (#23797) by @prastoin
- Six npm packages with critical or high CVEs are patched in the server image: undici (v6/v7 to v8), js-yaml, sharp, brace-expansion, ip-address, and dompurify. Self-hosters should update; the undici major bump may affect custom code using it directly. (#23813) by @charlesBochet
- CALL_RECORDING_SUMMARY and CALL_RECORDING_TRANSCRIPT widget types are registered with their configuration schemas; UI components come in a follow-up. (#23864) by @ehconitin
- subdomainMinLength is removed from ClientConfig; nothing has read this field since v2.30.0 and client-side parsing of it can be dropped. (#23915) by @ijreilly
🏗️ Internal changes (14)
- Make subdomain minimum length configurable via env var (#23209) by @ijreilly
- Add cookie-session integration test suite (#23715) by @prastoin
- chore(codeowners): guard .claude, .mcp.json and CLAUDE.md (#23734) by @prastoin
- Update website release notes through 2.26 (#23736) by @Bonapara
- Run the people-data-labs spec suite in CI (#23743) by @bosiraphael
- Keep the token pair as a fallback after switching to cookie auth (#23755) by @FelixMalfait
- feat(workflow): dispatch automated triggers from core behind a flag (#23775) by @thomtrp
- Refactor outbound email content compilation (#23782) by @FelixMalfait
- fix(workflow): make core-consistency drift check trustworthy for rollout (#23807) by @thomtrp
- Extend live update coverage: workflow draft updates and side-panel records (#23830) by @FelixMalfait
- refactor(front): move workflow run step logs into workflow-actions (#23841) by @thomtrp
- Revert AI chat chips to the [[kind:...:label]] syntax (#23852) by @bosiraphael
- ci(pr-review): stop a skipped label dispatch from cancelling the open dispatch (#23856) by @charlesBochet
- Update terms of service (#23906) by @FelixMalfait
🎉 New contributors
- @blockgroot made their first contribution in #23758
- @Dumeng made their first contribution in #23913
Full changelog (130 PRs)
- Make subdomain minimum length configurable via env var (#23209) by @ijreilly
- Enforce row-level permissions on joined relations (#23369) by @bosiraphael
- Add messages support to runAgent for multi-turn bot conversations (#23395) by @abdulrahmancodes
- feat(run-agent): let apps run an agent on behalf of a workspace member (#23470) by @abdulrahmancodes
- Add a book-a-call onboarding step for qualified leads (#23521) by @bosiraphael
- feat(twenty-slack): link records and format the assistant reply footer (#23539) by @abdulrahmancodes
- Stop showing raw chunk load errors (#23571) by @bosiraphael
- Fix grouping a view that can no longer be changed or removed (#23619) by @abdulrahmancodes
- Fan out Fireflies backfill into batched jobs (#23622) by @ehconitin
- fix: allow app-manifest RECORD_TABLE widgets to reference a view by universal identifier (#23634) by @thomtrp
- Feat/advanced text editor capability presets (#23657) by @ijreilly
- fix(twenty-front): expand HTML preview viewport in DocumentViewer (#23668) (#23676) by @Shinu-Cherian
- webhook subscriptions error handling (#23707) by @neo773
- Add cookie-session integration test suite (#23715) by @prastoin
- Hide onboarding credits when billing is disabled (#23717) by @bosiraphael
- fix(front): show relation value chip (Me / record names) in advanced filters (#23718) by @charlesBochet
- feat(slack): cache the Slack bot user id at connect time (#23726) by @abdulrahmancodes
- chore: bump version to 2.28.0 (#23730) by @twenty-pr
- fix(settings): unbreak the AI tools table and validate page-level graphql documents (#23731) by @FelixMalfait
- chore(codeowners): guard .claude, .mcp.json and CLAUDE.md (#23734) by @prastoin
- Update website release notes through 2.26 (#23736) by @Bonapara
- Accept a singleton or filter in the GraphQL filter walker (#23738) by @bosiraphael
- feat(workflow): repair orphan core workflow versions via upgrade command (#23739) by @thomtrp
- i18n - translations (#23740) by @github-actions
- Run the people-data-labs spec suite in CI (#23743) by @bosiraphael
- Continue the workspace setup chat in the side panel when navigating away (#23744) by @bosiraphael
- i18n - translations (#23746) by @github-actions
- i18n - translations (#23748) by @github-actions
- Ignore expired invitations in invitation lookups (#23749) by @prastoin
- Add IS_FEATURE_FLAG_MANAGEMENT_ENABLED to unlock feature flag toggling outside cloud (#23750) by @FelixMalfait
- feat(server): export event-loop delay and workspace-cache recompute metrics (#23751) by @charlesBochet
- Dnd library migration fixes and changes (#23752) by @git-init-priyanshu
- feat(slack): release the Slack team claim when the connection goes away (#23753) by @abdulrahmancodes
- Keep the token pair as a fallback after switching to cookie auth (#23755) by @FelixMalfait
- fix(front): only show Discard Draft when workflow has a published version (#23756) by @thomtrp
- Add global admin panel chat list with onboarding filter and enriched transcript (#23757) by @bosiraphael
- fix(workflow): reject if-else branches with a dangling filterGroupId (#23758) by @blockgroot
- Restore the install-apps onboarding step when enqueuing its job fails (#23759) by @bosiraphael
- Standardize dragged element feedback (#23772) by @Bonapara
- feat(workflow): dispatch automated triggers from core behind a flag (#23775) by @thomtrp
- feat(server): instrument the local metadata cache and cap heavy providers by entry count (#23778) by @charlesBochet
- Proxy API routes through the vite dev server to keep local dev same-origin (#23779) by @prastoin
- Restrict which workspace fields can be updated before activation (#23781) by @prastoin
- Refactor outbound email content compilation (#23782) by @FelixMalfait
- fix(twenty-front): stop field widget layout dropdown from crashing th… (#23784) by @FelixMalfait
- Make the call recorder transcript provider an application variable (#23789) by @ehconitin
- fix(twenty-server): compose email from the caller's own connected account (#23793) by @charlesBochet
- i18n - translations (#23794) by @github-actions
- i18n - docs translations (#23796) by @github-actions
- Add OpenTelemetry metrics to workspace migration build and run (#23797) by @prastoin
- Fix stray bracket after AI chat chips (#23798) by @bosiraphael
- i18n - translations (#23800) by @github-actions
- i18n - translations (#23803) by @github-actions
- fix(twenty-server): stop the yarn-install Lambda from running out of memory (#23805) by @charlesBochet
- i18n - translations (#23806) by @github-actions
- fix(workflow): make core-consistency drift check trustworthy for rollout (#23807) by @thomtrp
- Fix workflow email body overflowing into the attachments section (#23810) by @ijreilly
- Fix AI campaign editing: tool auth context in workers and live editor resync (#23811) by @FelixMalfait
- Stop classifying Recall bot-detection timeouts as NOT_RECORDED in call-recorder (#23812) by @ehconitin
- Bump vulnerable dependencies flagged by ECR image scanning (#23813) by @charlesBochet
- Fix widget view save paths dropping relationTargetFieldMetadataId (#23814) by @FelixMalfait
- Add nested relation Field widgets on record page layouts (#23815) by @FelixMalfait
- Cancel drag activation when the drag source unmounts mid-gesture (#23816) by @FelixMalfait
- Report a sending domain as pending while SES waits for the DKIM records (#23818) by @ijreilly
- fix(front): apply advanced filter value and operand edits after a fresh page load (#23819) by @charlesBochet
- chore: bump version to 2.29.0 (#23820) by @twenty-pr
- Feat/list view type (#23829) by @neo773
- Extend live update coverage: workflow draft updates and side-panel records (#23830) by @FelixMalfait
- Make AI chat streaming render cost independent of message length (#23831) by @FelixMalfait
- Fix client-side record matching for nested relation filters (#23832) by @FelixMalfait
- i18n - translations (#23834) by @github-actions
- slack: answer empty requests and nudge lapsed threads (#23835) by @abdulrahmancodes
- feat(slack): strip the bot's own mention anywhere in the request text (#23837) by @abdulrahmancodes
- feat(slack): adopt the native agent surface, starting with suggested prompts (#23838) by @abdulrahmancodes
- chore: sync AI model catalog from models.dev (#23839) by @github-actions
- Rename credits "Increase" button to "Manage" (#23840) by @FelixMalfait
- refactor(front): move workflow run step logs into workflow-actions (#23841) by @thomtrp
- Scope sessions and application authorizations to the workspace (#23843) by @FelixMalfait
- i18n - translations (#23844) by @github-actions
- i18n - translations (#23845) by @github-actions
- Fix missing border below the last record table row (#23846) by @FelixMalfait
- i18n - docs translations (#23851) by @github-actions
- Revert AI chat chips to the [[kind:...:label]] syntax (#23852) by @bosiraphael
- i18n - translations (#23853) by @github-actions
- fix(twenty-front): keep auto-select model preselection instead of discarding it (#23854) by @charlesBochet
- fix(front): run a single Monaco instance across the app (#23855) by @FelixMalfait
- ci(pr-review): stop a skipped label dispatch from cancelling the open dispatch (#23856) by @charlesBochet
- Give AWS SDK clients an explicit request timeout (#23857) by @charlesBochet
- fix(twenty-server): notify SSE subscribers when an update leaves their filtered view (#23858) by @charlesBochet
- Self-host every font instead of loading them from Google (#23859) by @FelixMalfait
- i18n - translations (#23860) by @github-actions
- Add call recording transcript and summary widget types (#23864) by @ehconitin
- Escape JSON-LD payloads before inlining them in a script tag (#23865) by @FelixMalfait
- Strip template elements from the HTML block preview (#23866) by @FelixMalfait
- Clear to-one relation key instead of storing {id: null} in record workflow steps (#23869) by @thomtrp
- Revert "Make subdomain minimum length configurable via env var" (#23871) by @ijreilly
- i18n - translations (#23873) by @github-actions
- fix(front): preserve currency decimals when opening the field editor (#23874) by @thomtrp
- i18n - docs translations (#23878) by @github-actions
- feat(server): export V8 GC pause and heap metrics (#23879) by @charlesBochet
- perf(server): compact and cold-store workspace cache payloads behind a feature flag (#23880) by @charlesBochet
- Gate the workspace setup AI chat to workspace creators (#23881) by @bosiraphael
- feat(server): export local cache entry count per provider (#23883) by @charlesBochet
- Show only the record title in the mobile page header (#23902) by @FelixMalfait
- i18n - translations (#23905) by @github-actions
- Update terms of service (#23906) by @FelixMalfait
- Expire webhook subscriptions whose refresh token is dead (#23907) by @neo773
- i18n - translations (#23912) by @github-actions
- fix(twenty-front): resolve the command menu item i18n rendering issue (#23913) by @Dumeng
- chore: bump version to 2.30.0 (#23914) by @twenty-pr
- Remove deprecated subdomainMinLength from ClientConfig (#23915) by @ijreilly
- i18n - translations (#23916) by @github-actions
- i18n - docs translations (#23917) by @github-actions
- i18n - docs translations (#23919) by @github-actions
- i18n - translations (#23922) by @github-actions
- i18n - docs translations (#23924) by @github-actions
- [BREAKING CHANGE] refactor(server): migrate the metadata API off nestjs-query (#23925) by @abdulrahmancodes
- Add full-page AI chat mode with records as side panel artifacts (#23929) by @FelixMalfait
- Add List View lab icon and make lab feature flag icons mandatory (#23930) by @FelixMalfait
- i18n - translations (#23939) by @github-actions
- Make LIST a widget layout instead of a table in disguise (#23940) by @FelixMalfait
- i18n - translations (#23942) by @github-actions
- Persist multi-item field changes as they are committed (#23943) by @FelixMalfait
- i18n - docs translations (#23944) by @github-actions
- feat(email-composer): reorder, move and multi-select recipient chips (#23947) by @FelixMalfait
- feat(email-composer): restyle the composer to the Send Email design (#23948) by @FelixMalfait
- Fix record title crash on non-string value (trim is not a function) (#23950) by @thomtrp
- i18n - translations (#23952) by @github-actions
- fix(server): signal truncation to AI agent in find_many record tool (#23955) by @thomtrp
- fix(security): bump js-yaml, sharp, brace-expansion, ip-address, dompurify (#23963) by @charlesBochet
Full Changelog: twenty/v2.27.0...twenty/v2.30.0