Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion content/configuration/email.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description: Configuration for email settings and templates.
| Variable | Description | Default Value |
| -------------------- | ------------------------------------------------------------------------ | ------------- |
| `EMAIL_VERIFY_SETUP` | Check if email setup is properly configured. | `true` |
| `EMAIL_TRANSPORT` | What to use to send emails. One of `sendmail`, `smtp`, `mailgun`, `ses`. | `sendmail` |
| `EMAIL_TRANSPORT` | What to use to send emails. One of `sendmail`, `smtp`, `mailgun`, `ses`, `mailtrap`. | `sendmail` |

Based on the `EMAIL_TRANSPORT` used, you must also provide additional variables.

Expand Down Expand Up @@ -51,6 +51,19 @@ Based on the `EMAIL_TRANSPORT` used, you must also provide additional variables.
| `EMAIL_SES_CREDENTIALS__SECRET_ACCESS_KEY` | Your AWS SES secret key. | |
| `EMAIL_SES_REGION` | Your AWS SES region. | |

### Mailtrap

Directus sends through the official [`mailtrap`](https://www.npmjs.com/package/mailtrap) package, which supports live sending, the sandbox testing inbox, and the bulk stream.

| Variable | Description | Default Value |
| ------------------------------ | ---------------------------------------------------------------------------------------------------- | ------------- |
| `EMAIL_MAILTRAP_TOKEN` | An API token from [your Mailtrap account](https://mailtrap.io/api-tokens). Required. | |
| `EMAIL_MAILTRAP_SANDBOX` | Send to a testing inbox instead of real recipients. Requires `EMAIL_MAILTRAP_TEST_INBOX_ID`. | `false` |
| `EMAIL_MAILTRAP_TEST_INBOX_ID` | The testing inbox to send to when `EMAIL_MAILTRAP_SANDBOX` is enabled. | |
| `EMAIL_MAILTRAP_BULK` | Send through the bulk stream. Cannot be combined with `EMAIL_MAILTRAP_SANDBOX`. | `false` |

`EMAIL_MAILTRAP_TOKEN` is required. Directus throws an error when it creates the transport without a token, because the Mailtrap transport cannot verify its configuration ahead of the first send.

## Email Templates

Templates can be used to add custom templates for your emails, or to override the system emails used for things like resetting a password or inviting a user.
Expand Down
18 changes: 18 additions & 0 deletions content/guides/01.data-model/1.collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,28 @@ Once a collection is created, there are a number of configuration options availa
- **Icon**: icon used throughout the Data Studio when referencing this collection.
- **Color**: color for the icon, shown in the navigation bar and page headers.
- **Display Template**: display templates are used to represent an item in relationship fields - for example to show the value of the `Name` field when displaying a post's author.
- **Status**: set the collection to **Active** or **Inactive**. See [Inactive Collections](#inactive-collections) for what an inactive collection can and cannot do.
- **Hidden**: toggle whether the collection should be globally hidden in the Data Studio.
- **Singleton**: toggle to bypass the collection page and take users to the single item details page.
- **Collection Naming Translations**: translate the collection name across multiple languages. When the default language is changed, the relevant translation will be used throughout the Data Studio.

### Inactive Collections

An **inactive** collection keeps its table, fields, and data, but cannot be used for content. Collections are normally deactivated through the licensing [resolution flow](/licensing/overview#resolution-flow) when an instance is over its collection limit, and this is how Directus brings usage back within a plan's limits without deleting anything.

While a collection is inactive:

- The Data Studio shows it in the data model, but it cannot be selected or opened for content. Fields that relate to an inactive collection are disabled in forms, layouts, and Insights panels.
- API reads and writes fail with a [`COLLECTION_INACTIVE`](/guides/connect/errors) error (`403`) for users who have permission on the collection, and a generic `FORBIDDEN` error for everyone else. This applies to REST, GraphQL, WebSockets, flow operations, and relational writes nested in another collection's payload.
- Queries that read, filter, or sort through a relation into the inactive collection are rejected. Wildcard field selections skip one-to-many and many-to-any aliases that target it, while a many-to-one field keeps returning its foreign key value.
- Schema management still works, so you can change its fields, relations, and settings.

To reactivate a collection, open its settings and set **Status** to **Active**. If your instance is at its collection limit, deactivate another collection or [add a license](/licensing/overview) that covers your usage first.

::callout{icon="i-lucide-info"}
System collections are never treated as inactive, even if their status is set to inactive in the database.
::

### Content Versioning

Content versioning is used on the [item page](/guides/content/editor) and allows teams to create and manage different versions of their content. There are several reasons to use content versioning, including drafting content without publishing it, and more ways to collaborate with others.
Expand Down
1 change: 1 addition & 0 deletions content/guides/04.connect/5.errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ The `code` value in `extensions` lets you handle errors programmatically without

| Error Code | Status | Description |
| ----------------------------- | ------ | --------------------------------------------------------------------------------- |
| `COLLECTION_INACTIVE` | 403 | The collection is inactive and can't be read or written. See [Inactive Collections](/guides/data-model/collections#inactive-collections). |
| `CONTAINS_NULL_VALUES` | 400 | A field can't be set to non-nullable because existing rows contain null values. |
| `CONTENT_TOO_LARGE` | 413 | Uploaded content exceeds the configured size limit. |
| `EMAIL_LIMIT_EXCEEDED` | 429 | The email sending limit has been hit. |
Expand Down
2 changes: 1 addition & 1 deletion content/licensing/1.overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ If your instance exceeds its entitlements — for example, after downgrading a p
- **Limit-based** entitlements show your current usage, the new limit, and a list of candidate records to deactivate.
- **Feature-gated** entitlements (such as SSO) show any blockers that must be resolved before the feature can be disabled — for example, admin accounts that need an email and password before SSO can be turned off.

Selecting candidates and submitting the resolution brings the instance back within its entitlements. Resources are deactivated rather than deleted, and admins are always excluded from seat resolution.
Selecting candidates and submitting the resolution brings the instance back within its entitlements. Resources are deactivated rather than deleted, and admins are always excluded from seat resolution. Deactivated collections are set to an **Inactive** status, which blocks reads and writes while keeping the table and its data. See [Inactive Collections](/guides/data-model/collections#inactive-collections).

::callout{icon="i-lucide-info"}
**SSO users after deactivation**
Expand Down
33 changes: 33 additions & 0 deletions content/releases/2.changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@ Each month, some of the Directus team talk through what’s new including core r

[Watch The Changelog on Directus TV.](https://directus.com/tv/the-changelog)

## September 2026

- [Directus 12.4.0](https://github.com/directus/directus/releases/tag/v12.4.0) has several potential breaking changes: inactive collections now reject API reads and writes with a `COLLECTION_INACTIVE` error, update and delete by query enforce read permissions when resolving the affected items, the map layout and interface require WebGL2, `@directus/themes` requires `@unhead/vue` 3, and a number of system type definitions in `@directus/types` and `@directus/sdk` were corrected. Review the [full list](/releases/breaking-changes/version-12#version-1240) before upgrading.
- Moved Flows into their own module in the module bar. The new [Manage Flows](/guides/flows/manage-flows) page adds folders, search and filtering, duplication, and import and export of Flows.
- A manual Flow can now [hide its own button](/guides/flows/triggers#manual), so it runs only from a **Button Links** interface configured to trigger it.
- The **Send Email** operation now accepts a [**From Name**](/guides/flows/operations#send-email), shown to recipients as the sender name.
- Added a native [Mailtrap](/configuration/email#mailtrap) email transport, configured with `EMAIL_TRANSPORT=mailtrap`. It supports live sending, sandbox testing inboxes, and the bulk stream.
- Inactive collections are now [disabled in the Data Studio](/guides/data-model/collections#inactive-collections). They stay visible, but cannot be selected or used until reactivated.
- Fixed relational edits made in quick succession saving duplicate entries for the same row, and fixed the block editor flickering while editing a version.
- Fixed `FilesService.uploadOne` so client payloads can no longer overwrite system fields such as `uploaded_by` and `created_on`, and fixed `uploaded_on` not being set for TUS uploads.
- Fixed foreign key constraints being dropped when updating a relation with a partial payload, and fixed schema diff generation for nested metadata additions and removals.
- Fixed the date picker showing English weekday names and starting the week on Sunday regardless of the user's language.
- Fixed Oracle date part functions returning zero-padded strings, `year()` returning the ISO week-numbering year, and `json()` returning quoted strings for scalar values.

## August 2026

- [Directus 12.3.0](https://github.com/directus/directus/releases/tag/v12.3.0) has several potential breaking changes: the **Update Items** and **Delete Items** flow operations no longer target every item when their key and query are empty, the storage driver's `exists()` method throws on connection failures instead of returning `false`, `ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION` now defaults to `6000`, and the Docker image runs the bundled `pm2`. Review the [full list](/releases/breaking-changes/version-12#version-1230) before upgrading.
- Introduced the [`@directus/cli`](/guides/environment-sync) package (`d6s`), a client-side CLI that syncs schema and configuration between Directus instances through committed JSON files, with `sync pull`, `sync diff`, `sync push`, and an interactive wizard.
- Added search-first tool discovery for the AI Assistant and MCP tools, with the schema tool pinned as a root chat tool.
- Added a caption field to the WYSIWYG image drawer, which wraps the image in a `<figure>` with a `<figcaption>`.
- Item and drawer headers now append the collection name to the display template.
- Fixed storage connections leaking when asset requests were cancelled or a transformation failed.

This month we had two releases.
- [Directus 12.3.1](https://github.com/directus/directus/releases/tag/v12.3.1) is a patch release with no breaking changes.
- Added `countFilterListeners`, `countActionListeners`, and `countInitListeners` methods to the `emitter`, exposing the number of registered handlers for each event.
- Fixed the SDK's `unsubscribe()` not removing subscriptions, which caused them to persist across reconnects and accumulate for the lifetime of the client.
- Fixed the WebSocket heartbeat leaking a `websocket.message` listener on each ping when a client failed to respond in time.
- Fixed GraphQL query fragments returning null fields.
- Fixed public registration verification using the provided email instead of the stored one.
- The `@directus/cli` now strips `project_id` when pulling settings, so a sync no longer copies one instance's identity onto another.
- Removed `user_created` and `date_created` from the recommended `update` permissions for `directus_shares`.

## July 2026

- [Directus 12.2.0](https://github.com/directus/directus/releases/tag/v12.2.0) has a breaking change: the WYSIWYG (rich text) interface is now built on [Tiptap](https://tiptap.dev) instead of TinyMCE. Existing fields keep working, but stored HTML is normalized to supported markup on first edit and the **Options Override** (`tinymceOverrides`) option is deprecated. See the [Version 12 breaking changes](/releases/breaking-changes/version-12#wysiwyg-editor-rebuilt-on-tiptap).
Expand Down
57 changes: 57 additions & 0 deletions content/releases/3.breaking-changes/3.version-12.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,63 @@ title: Version 12
description: Breaking changes may require action on your part before upgrading.
---

## Version 12.4.0

### Inactive collections reject reads and writes

A collection whose status is **Inactive** can no longer be read or written through the API. Previously the status only affected how the collection appeared in the Data Studio. Requests against an inactive collection now fail with a [`COLLECTION_INACTIVE`](/guides/connect/errors) error (`403`) for users who have permission on the collection, and with a generic `FORBIDDEN` error for everyone else.

The enforcement applies to REST, GraphQL, WebSockets, flow operations, and relational writes nested inside another collection's payload. Queries that read, filter, or sort through a relation into an inactive collection are rejected as well. Wildcard field selections (`*` and `*.*`) drop one-to-many and many-to-any aliases that point at inactive collections, while a many-to-one field keeps returning its foreign key value.

Schema management is unaffected, so you can still change an inactive collection's fields, relations, and settings. In the Data Studio, inactive collections remain visible but cannot be selected or used.

Collections are deactivated through the licensing [resolution flow](/licensing/overview#resolution-flow) when an instance is over its collection limit. To use one again, set its status back to **Active** in the collection settings. See [Inactive Collections](/guides/data-model/collections#inactive-collections) for details.

### Update and delete by query enforce read permissions

`updateByQuery` and `deleteByQuery` previously resolved the affected item keys without applying the caller's read permissions. They now resolve the affected items with read permissions enforced, which changes what a request can touch:

- Read access to the collection's primary key field is required. Roles with update or delete permissions but no read permission on the primary key now receive a `FORBIDDEN` error.
- The affected items are limited to those the role can read, regardless of its update or delete rights. Items that a role can update but cannot read are skipped.
- Fields used in the query's `filter` and `sort` must be readable by the role, because the query now runs as an ordinary read.
- Saving nested one-to-many items requires read access on the child collection.

This applies to `PATCH` and `DELETE` requests that pass a `query`, to the equivalent GraphQL mutations, and to the **Update Items** and **Delete Items** flow operations when they run with a non-admin accountability. Review policies that grant update or delete access without a matching read permission.

### Map layout and map interface require WebGL2

The Data Studio's map layout and map interface now run on [MapLibre GL JS](https://maplibre.org) 6, which requires WebGL2. Browsers that only support WebGL1, chiefly Safari 14 and earlier and older Android devices, no longer render maps. All other Studio functionality is unaffected in those browsers.

Dragging the map with a pointer or mouse wheel still pins the hovered item popup to the cursor. Touch drags no longer reposition the popup.

### `@directus/themes` requires `@unhead/vue` 3

The `@directus/themes` package now lists `@unhead/vue` 3.x as a peer dependency. If you pair the package with your own Unhead instance, upgrade it and follow the Unhead [v2](https://unhead.unjs.io/docs/migration-guide/v2) and [v3](https://unhead.unjs.io/docs/vue/migration-guide/v3) migration guides. Note that `createHead` is now imported from `@unhead/vue/client` and template params require registering `TemplateParamsPlugin`.

### System type definitions corrected

Several type definitions in `@directus/types` and `@directus/sdk` were out of date with the fields they describe. TypeScript projects that read these fields without a null check may fail to compile after upgrading.

In `@directus/types`, the following are now nullable:

- `Comment.user_created` and `Comment.user_updated`
- `FlowRaw.user_created`
- `OperationRaw.options` and `OperationRaw.user_created`
- `Preset.collection`
- `Role.description`
- `ContentVersion.hash`
- `Share.name`, `Share.role`, `Share.password`, `Share.user_created`, `Share.date_created`, and `Share.times_used`

`Policy.enforce_tfa` in `@directus/types` is no longer nullable.

In `@directus/sdk`:

- `DirectusRole.parent`, `DirectusVersion.hash`, `DirectusRelation.meta`, and `DirectusRelation.schema` are now nullable.
- `DirectusPolicy.ip_access` and `DirectusRelation.meta.one_allowed_collections` are now typed as `string[]` instead of `string`.
- The unused helper types `ToTuple` and `TupleToUnion` have been removed.

The SDK also validates that `readRelationByCollection`, `createField`, `deleteCollection`, `utilsExport`, `utilsImport`, `utilitySort`, `triggerFlow`, and `readShareInfo` receive non-empty parameters, and throws before sending a request when they do not.

## Version 12.3.0

### Update and Delete operations no longer target all items by default
Expand Down
Loading