DatoCMS changelog for new features and general improvements

CLI Integrations New

## [Introducing DatoCMS Agent Skills](https://www.datocms.com/product-updates/introducing-datocms-agent-skills.md)

[date: 2026-05-26T12:15:03.392+02:00]

DatoCMS Agent Skills are a set of markdown-based playbooks for AI coding agents. They give your agent the context it needs help you develop your projects correctly with DatoCMS: the right patterns, the right conventions, loaded on demand based on what you're working on.

**Quick Links:**

-   [Check out the docs and installation guide](https://www-draft.datocms.com/docs/agent-skills) to get started with your tooling preference.
-   The whole thing is open source at [github.com/datocms/agent-skills](https://github.com/datocms/agent-skills).
    
-   All [skills are also listed on the skills.sh directory](https://skills.sh/?q=datocms).
-   [Check out the blog post](https://www.datocms.com/blog/the-new-datocms-agent-skills.md) for a few further details.
    

Its' helpful knowing what these skills are capable of, so here's a TLDR of what they can currently do:

-   **Content modeling** — schema-design decisions: model vs block, references vs embedded blocks, taxonomies, field shapes, validators, editor appearances.
-   **Reading content** — GraphQL queries against the Content Delivery API: filters, pagination, localization, modular content, Structured Text, responsive images, SEO metadata, typed queries with gql.tada or codegen.
    
-   **Writing content & automation** — programmatic CMA scripts: record CRUD, bulk imports/exports, asset uploads, environment forks and promotions, webhooks, roles and tokens, scheduled publishing, audit logs.
-   **Workflows best practices** — migrations, schema-type generation, typed CMA scripts, environment operations, CI/CD pipelines.
    
-   **Frontend integrations** — draft mode, Web Previews, Visual Editing, real-time preview subscriptions, cache-tag invalidation, SEO/sitemap wiring across Next.js, Nuxt, SvelteKit, Astro.
-   **Plugin** **development** — create a brand-new plugin from scratch with the Vite/React structure, picking the initial surfaces (field extensions, config screens, sidebars, pages, asset sources).
    

**Installation**

For Claude Code and Codex:

```bash
/plugin marketplace add datocms/agent-skills/plugin install datocms@datocms-skills
```

For Cursor, Windsurf, Copilot, and others:

```bash
npx skills add datocms/agent-skills
```

Skills are open source at [github.com/datocms/agent-skills](https://github.com/datocms/agent-skills) and listed on the [skills.sh directory](https://skills.sh/?q=datocms).

[Read the Agent Skills docs](https://www.datocms.com/docs/agent-skills.md) for full installation options and usage.

CLI Content Management API API Clients New

## [CLI and CMA Improvements: cma:script, Stricter Types, and Dastdown](https://www.datocms.com/product-updates/cli-and-cma-improvements-cmascript-stricter-types-and-dastdown.md)

[date: 2026-05-18T15:41:44.751+02:00]

The last couple of weeks brought a set of converging improvements across the CLI, the JS CMA client, and the structured-text packages.

#### `datocms schema:generate` now exposes runtime ID and REF constants

The schema types generator now emits `Schema.Article.ID` and `Schema.Article.REF` alongside the existing TypeScript types. No more hardcoded item-type ID strings drifting out of sync across environments!

```typescript
// type position: the model's TS shape, as beforeconst article = await client.items.find<Schema.Article>(id);// value position: the model's id and ref, generated from your projectawait client.items.create({  item_type: Schema.Article.REF,  // …});if (item.relationships.item_type.data.id === Schema.Article.ID) {  // …}
```

#### New `FieldValue<T, K>` helpers

Three new helpers let you derive a field's type directly from an existing record or block for the full read/write cycle:

-   `FieldValue<T, K>` — standard response shape
-   `FieldValueInNestedResponse<T, K>` — what you get when reading with `nested: true`
    
-   `FieldValueInRequest<T, K>` — what `client.items.create` / `update` expects
    

`T` accepts anything item-shaped like a fetched record, a nested block, or an `ItemTypeDefinition` directly. The typical flow this unlocks: read a record with `nested: true`, iterate and mutate its nested blocks, and write the result back. Typing the accumulator takes one line:

```typescript
const page = await client.items.find<LandingPage>(id, { nested: true });const sections: FieldValueInRequest<typeof page, 'sections'> = [];
```

#### New `isBlockWithItemOfType` predicate

`isBlockWithItemOfType` (and its `isInlineBlockWithItemOfType` counterpart) from `datocms-structured-text-utils` narrows a block node to a specific model shape using its item type ID. Works in `findFirstNode`, `mapNodes`, or standard conditionals, giving you typed access to `.item.attributes` directly without extra casting.

```typescript
// inline guardif (isBlockWithItemOfType(Schema.CtaBlock.ID, node)) {  // node.item.attributes is now typed as CtaBlock}// curried predicateconst firstCta = findFirstNode(content, isBlockWithItemOfType(Schema.CtaBlock.ID));
```

#### Edit Structured Text as plain text with `datocms-structured-text-dastdown`

The new `datocms-structured-text-dastdown` package gives you a lossless, markdown-flavored serialization for DatoCMS Structured Text documents. Serialize to plain text, edit it however you like (string manipulation, regex, LLM rewrite), and parse back instead of walking the AST.

Best fit: text-heavy content like articles, docs, and chapters where edits are textual and may cross node boundaries. Not for landing pages made of opaque blocks — referenced blocks stay opaque in the serialized form, so you can move or remove them but not edit their internals at this layer.

A typical round-trip would look like:

```typescript
import { parse, serialize } from 'datocms-structured-text-dastdown';const cur = await client.items.find<Schema.Article>('article-id', { nested: true });const text = serialize(cur.body);const edited = text.replace(/Acme Corp/g, '**Acme Inc.**');const body = parse(edited, cur.body);await client.items.update<Schema.Article>('article-id', { body });
```

#### `mapNodes` now supports full tree rewrites

`mapNodes` and `mapNodesAsync` from `datocms-structured-text-utils` now support structural transforms, not just 1:1 node mapping. The return value controls what happens:

-   Return a single node: direct replacement
-   Return an array: splat into the parent's children
    
-   Return `null` or `undefined`: remove the node entirely
    

#### New agent-targeted commands

We've also shipped `datocms cma:script`, `datocms schema:inspect`, and enhanced `datocms cma:docs` - commands designed to be used by your agents directly. These are documented in the CLI repo and will evolve as agent tooling matures.

---

To update: `npm i -g datocms` for CLI changes, `npm i @datocms/cma-client-node@latest` for client changes, `npm i datocms-structured-text-dastdown` for the new package.

CLI New

## [CLI: \`npx datocms\` now Just Works](https://www.datocms.com/product-updates/cli-npx-datocms-now-just-works.md)

[date: 2026-04-29T16:42:07.831+02:00]

The DatoCMS CLI is now published on npm as `datocms` — an unscoped package with the same name as its binary. The scoped `@datocms/cli` package is still around as a thin alias, so existing setups keep working unchanged.

**The main win:** `npx datocms` now "Just Works" in every context — whether the package is installed locally, globally, or not installed at all.

### Why this matters

Until now, running `npx datocms projects:list` inside a project that had `@datocms/cli` installed could result in a confusing error:

```bash
$ npx datocms projects:listnpm error Missing script: "datocms"
```

This is an npm/npx quirk: when the package name and binary name differ, it can't always find the CLI and bails out. `pnpm`, `yarn`, and `bun` all handled this case correctly — it's only npm users who got bitten. Now that the package and binary share the same name, the most natural command just works. As a bonus, we now own the unscoped `datocms` name on npm, so it can't get squatted.

### What changes for you

-   **New users:** run `npx datocms ...` or `npm i -g datocms`. That's it.
-   **Existing users on** **`@datocms/cli`****:** nothing to do. Your setup keeps working, and you'll transparently pick up the new `datocms` package as a dependency. Migrating is a one-line change in `package.json` whenever you feel like it.
    
-   **Existing migration files** importing from `@datocms/cli/lib/cma-client-node`: still work, no changes needed. New migration files use `datocms/lib/cma-client-node`.
    

Head over to the [docs on configuring the CLI](https://www.datocms.com/docs/cli.md) for installation details.

Content Management API

## [CMA limit raised for Developer Plan](https://www.datocms.com/product-updates/cma-limit-raised-for-developer-plan.md)

[date: 2026-04-20T11:08:34.171+02:00]

We have raised the CMA limit of the developer plan from 10K to 25K monthly API calls, to make it easier to get started with projects on the free plan.

CLI New

## [CLI: Easier (and safer) project linking with OAuth](https://www.datocms.com/product-updates/cli-easier-and-safer-project-linking-with-oauth.md)

[date: 2026-04-14T11:48:08.092+02:00]

Setting up the DatoCMS CLI used to involve a clunky ritual of creating an API token in project settings, copying it, pasting it into an environment variable, and hoping you got it right. Repeat for every project. And since teams shared the same token, there was no way to tell *who* actually performed a given action in audit logs.

**OAuth login is now the recommended way to authenticate.** The new setup is fully guided: `datocms login` opens your browser, `datocms link` lets you search and select a project interactively with no tokens to copy, and no environment variables to configure. Every API call is tied to your personal identity, giving teams clear visibility over who made which changes.

During the authorization step, you can choose to grant the CLI access to all your projects or limit it to only the ones you select, so you stay in control of exactly which projects are exposed.

(Video content)

### New commands

-   `datocms login` authenticates your DatoCMS account via OAuth. It opens your browser for a secure login flow. If the browser can't be opened, it falls back to a manual URL flow.
-   `datocms link` connects the current directory to a specific DatoCMS project. The interactive flow walks you through choosing a workspace, searching for a project, and configuring migration settings. Once linked, every CLI command in that directory automatically resolves an API token using your OAuth credentials. No environment variables needed.
    
-   `datocms logout` to remove credentials.
-   `datocms whoami` to check which account you're logged in as.
    
-   `datocms unlink` to disconnect a directory from a project.
    

### What changes for existing users

Nothing breaks. The old `profile:set` and `profile:remove` commands still work, they just redirect to `link` and `unlink` under the hood. Existing scripts and CI/CD pipelines using `DATOCMS_API_TOKEN` or the `--api-token` flag are completely unaffected.

When a command needs an API token, the CLI now resolves it in this order: `--api-token` flag first, then environment variable, then linked project via OAuth. So your current setup always takes precedence.

### Getting started

You can upgrade the CLI to the latest version with:

```bash
npm install -g @datocms/cli@latest
```

For teams that want user-level audit trails, the migration is straightforward: each team member runs `datocms login` once, then `datocms link` in each project directory. From that point on, all commands use personal credentials automatically.

You can still use `DATOCMS_API_TOKEN` in CI/CD or anywhere OAuth login isn't practical. If you prefer a custom environment variable name, you can configure it during `datocms link`.

OAuth authorizations can be reviewed and revoked at any time from your [account settings](https://dashboard.datocms.com/personal-account/account), under "Authorized applications":

(Image content)

[Explore the docs](https://www.datocms.com/docs/cli.md) to get up to speed on Configuring the CLI.

Docs, Guides and Demos Plugins

## [Starter kits now ship with a plugin scaffold](https://www.datocms.com/product-updates/starter-kits-now-ship-with-a-plugin-scaffold.md)

[date: 2026-04-09T11:56:22.368+02:00]

The Astro and Next.js starter kits now come with a private DatoCMS plugin already wired up, giving you a ready-made foundation to start building custom editor experiences from day one, with no separate repo, and no manual installation.

Because the **Plugin SDK** and `datocms-react-ui` component library are built on React, this feature is available in the React-based starters (Astro and Next.js) but not in the SvelteKit or Nuxt starters.

### Build custom plugins right inside your project

The plugin lives alongside your site code and is served as a regular page (/private-datocms-plugin). On your first deploy it installs itself automatically via the post-deploy hook — zero manual setup in the dashboard. The source mirrors the structure of official DatoCMS plugins.

(Image content)

The included config screen links straight to the docs to help you get started:

-   [Plugin SDK docs](https://www.datocms.com/docs/plugin-sdk/introduction.md)
-   [Build your first plugin guide](https://www.datocms.com/docs/plugin-sdk/build-your-first-plugin.md)
    
-   [Hooks overview](https://www.datocms.com/docs/plugin-sdk/what-hooks-are.md)
    

### Astro starter upgraded to Astro 6

The Astro starter has also been upgraded to Astro 6 along with all official integrations (@astrojs/node, @astrojs/react, @astrojs/check) and @datocms/cli v4. The minimum Node version is now 22.

Images API Content Management API New

## [Automatic antivirus scanning for all Media Area uploads](https://www.datocms.com/product-updates/automatic-antivirus-scanning-for-all-media-area-uploads.md)

[date: 2026-04-07T18:14:16.415+02:00]

Every file uploaded to the DatoCMS Media Area is now automatically scanned for viruses and malware. No configuration, no opt-in or workflow changes required from your side.

### How it works

(Video content)

The moment a file is uploaded, a background scan is queued automatically. Editors can continue working since there's no blocking step or wait time. Within seconds, each file is assigned one of four statuses:

-   **Clean**: no threats detected and file is served normally
-   **Infected**: a threat was detected and the file is automatically quarantined
    
-   **Skipped:** the file exceeds the scanner's size or type limits and could not be assessed. Treat these files with appropriate caution
-   **Failed**: an error occurred during scanning, and will be retried automatically up to 6 times with exponential backoff. If it still cannot be scanned, then the file remains in a failed state
    

ℹ️ If an asset is replaced with a new version, the antivirus scan runs again automatically on the new file.

### Quarantined files

When a threat is detected, infected files are automatically quarantined and DatoCMS will:

1.  Remove the file from public storage
    
2.  Purge it from the CDN cache, and
    
3.  Keep the upload record visible in the Media Area so editors can see it was flagged, but the file URL will no longer serve any content
    

Editors should replace the asset to restore functionality.

For **projects using a custom storage bucket**, DatoCMS does not have permission to delete or move files from your storage. In this case, the file will still be accessible from your own bucket even after being flagged, and the upload record will be marked as infected, and editors will see the file path so they can remove it manually. The warning UI in the Media Area will reflect this.

### Dashboard Changes

Infected files are surfaced throughout the Media Area:

A **"Threat detected"** badge appears on the upload card in grid, masonry, and table views. On smaller cards, this collapses to an icon with a tooltip

(Image content)

Opening an infected file replaces the normal preview with a **warning screen** that explains the situation, shows the specific threat name (useful for investigation), and prompts the editor to replace the asset

(Image content)

For custom storage projects, the warning is adjusted to show the file path and advise manual removal from the bucket

Editors can filter uploads by antivirus status (clean, infected, skipped, failed, pending) directly in the Media Area search. This filter is **not available** in the Content Delivery API.

Scan results are delivered in real time with the antivirus status in the dashboard updating live without requiring a page refresh, and Webhooks are fired on status changes, so you can build integrations that react to scan results, for example, getting a Slack alert when an infected file is detected in your project.

### API Access

The antivirus status is also available on every upload object via the CMA, under a new `meta.antivirus` field:

```json
"meta": {  "antivirus": {    "status": "infected",    "scanned_at": "2026-03-27T18:51:00Z",    "threat_name": "Trojan.GenericKD.12345"  }}
```

The object includes the scan status, the timestamp of the last scan, and the threat name when applicable. It's worth knowing that antivirus scan results are preserved when forking environments, with no rescanning needed, and when duplicating a project, infected files are automatically excluded from the copy to prevent propagation.

[Refer to the docs](https://www.datocms.com/docs/general-concepts/media-area.md#antivirus-scanning) for more information on how this works.

Plugins New

## [Configurable \`hue\` property on Visual Editing](https://www.datocms.com/product-updates/configurable-hue-property-on-visual-editing.md)

[date: 2026-03-23T13:30:59.163+01:00]

When using the Visual Editing feature, the overlay color used to highlight editable areas can now be customized via a `hue` property (accepts values 0–359) in the configuration.

Previously, editable regions were always indicated with an orange highlight, which could create a usability problem for sites with orange-heavy designs, where the overlay could blend into the page.

(Image content)

All the SDKs now have a configurable `hue` property to allow the highlight to stand out regardless of your brand palette.

(Image content)

Get started with [Visual Editing from the docs](https://www.datocms.com/docs/visual-editing.md).

Security Images API

## [Introducing Permissions for Asset Collections](https://www.datocms.com/product-updates/introducing-permissions-for-asset-collections.md)

[date: 2026-03-10T13:50:05.408+01:00]

Building up on our earlier release for Asset Collections, you can now assign specific permissions for users to assets within Collections, including read, write, and a new dedicated `move` permission that controls whether users can move assets from one collection to another.

Permissions assigned to a collection are automatically inherited by its sub-collections, with inheritance rules clearly displayed in the UI.

(Video content)

To enable or change access for collections, go to settings and select Asset Permissions \> Collections when editing User Roles.

(Image content)

UI Improvement

## [Pre-filter linked records with saved filters](https://www.datocms.com/product-updates/pre-filter-linked-records-with-saved-filters.md)

[date: 2026-02-18T10:29:06.258+01:00]

If you've ever had editors accidentally linking the wrong records because the list was too broad, this one's for you. When linking records, the list of available items can get overwhelming, especially when it includes outdated or irrelevant records that editors shouldn't pick (like records still in drafts).

To address this, we've introduced a way to pre-filter linked records with saved filters, letting you control exactly which records appear when editors browse items to link.

**What's new?**

The standard editors for Single and Multiple Link fields (compact and expanded) now include a newsettings section. Here, you can pair each linked model with one of your saved shared filters, so editors only see the records that they should.

**How do I enable it?**

Head to the **Presentation** tab in your Link field settings. You'll see the new Applied Filters section right away: just pick a model, choose a filter, and you're done. If you need more filters, create more saved filters in the Content Area to configure another.

(Video content)

The only prerequisite is that you need at least one **shared** saved filter for the models you want to filter. Private filters aren't available here.