Skip to main content

Automating your content with Sanity's Agent Actions, Functions & Blueprints

Tired of stitching together external services for your Sanity workflows? This article explains how the new trio of Agent Actions, Functions, and Blueprints provides a native solution. Learn how to bring your automation logic in-house with this practical guide.

Automating your content with Sanity's Agent Actions, Functions & Blueprints

For a long time, we collectively treated the CMS as a "content store" - a convenient database with a UI where non-developers could edit text and images without, as we’d say, a pain in the ass. Its job was to hold things.

But over the past few years, Sanity has been aggressively pushing the boundaries of that definition. They transitioned from a simple content store into a true Content Operating System. It became a hub for structured data you can model, query, and connect in powerful new ways.

Yet, for all this progress, we as developers still hit a ceiling. When it came to a crucial part of the modern stack, automation, we had to look elsewhere. We found ourselves reaching for Vercel or Netlify functions to handle tasks that felt like they should belong inside Sanity. We needed to enrich content on publish, trigger a validation workflow or sync data with another service.

This approach worked, but it always felt like a workaround. It created a divide, forcing us to manage fragmented architectures and spread authentication tokens across platforms. Our logic lived in one place, and our content in another.

With the Spring Release, that is no longer the case. Sanity has finally delivered the missing piece. The introduction of native AI and serverless capabilities means the platform is no longer just a sophisticated content store - it has become a truly mature Content Engine. The logic and the content can now live together, closing the gap and fundamentally changing what's possible within a single, unified system.

AI & Automation Inside Sanity

So, what powers this new Content Engine? Sanity has delivered a trio of interconnected features that together create a complete and native ecosystem for automation. While still in their early stages, they already form a powerful foundation.

Think of it as a complete toolkit with three core components:

  1. Agent Actions (AI brains). This is the intelligence layer. Agent Actions are schema-aware AI instructions that can programmatically generate, transform, and translate your content. They aren't just simple text prompts - they understand your content models, which allows for incredibly precise and structured results.
  2. Functions (automation engine). This is the serverless power we've been waiting for. Functions are event-driven pieces of code that live right inside your Sanity project and react to content changes. Triggered by content operations like ⁠publish, ⁠create, or ⁠delete, these functions spring into action, running your code and eliminating the need for external webhooks or authentication boilerplate.
  3. Blueprints (infrastructure scaffolding). This is how you manage your new serverless capabilities using an infrastructure-as-code approach. Blueprints allow you to declaratively define, configure, and deploy your Functions, ensuring your automation setup is version-controlled, repeatable, and easy to manage from the command line.

The true power of this toolkit lies in how these components work together. You can use an Agent Action to define an AI task, then invoke it from a Function that triggers when a document is published, all managed and deployed via a Blueprint.

This is the integrated system that ends the workarounds. Let's break it down, starting with the AI brains of the operation: Agent Actions.

Agent Actions

The core of Sanity's new AI capabilities is powered by Agent Actions. Think of them as an intelligent, schema-aware layer that sits on top of your content. They know what a "blog post" is in your project, what fields it has, and how a "product" might reference a "category." This awareness allows them to perform tasks with a level of precision that was previously impossible without complex, hand-written code.

The Agent Actions are broken down into four distinct capabilities:

  • Generate: This is your creative engine. It goes beyond just text, allowing you to create entire documents from scratch, complete with structured data and even generated images. It's perfect for drafting a blog post from a title, populating a product description, or creating visual assets that match your content's context.
  • Transform: This is your intelligent editor. Instead of crude find-and-replace operations, Transform can modify existing documents with nuance. Use it to change an article's tone from formal to casual, summarize a long passage into a concise description, or update fields while preserving the integrity of the rest of the document.
  • Translate: Purpose-built for localization, this action handles both document-level and field-level translation. It's smart enough to protect brand names, technical terms, or other phrases you define, ensuring your core messaging remains consistent across languages.
  • Prompt: This is your direct line to an LLM, but with your Sanity content as the built-in context. It allows you to ask questions, analyze data, or run custom instructions against your documents without having to pipe the content to an external service.

These powerful actions are already being integrated across the Sanity ecosystem. The recently introduced Sanity MCP, which allows AI coding assistants to interact with Sanity projects, uses this new Agent Actions API under the hood.

For developers, getting started is straightforward. The actions are exposed as a new dedicated scope within the familiar @sanity/client, making them easy to call from anywhere you can run JavaScript. But how do we put them on autopilot? For that, we need an engine.

Functions & Blueprints

An AI brain is powerful, but its true potential is unlocked when it can act on its own. This is where Sanity Functions come in - the serverless engine we've been waiting for. This is the feature that finally allows us to stop outsourcing our automation logic to external platforms.

Functions are event-driven, single-purpose pieces of code that live directly within your Sanity project. They respond to content changes, such as publish, update, or delete - and execute your logic right on Sanity's infrastructure.

Functions CLI

Sanity has built a complete local-first workflow around Functions. The CLI provides a robust development environment:

  • Use sanity functions dev to run a local emulator for real-time development
  • Simulate trigger events with ⁠sanity functions test. It lets you invoke your function locally with a mock payload by passing either a local JSON file or the ID of a document in your dataset
  • When you need to debug a deployed function, you can stream its logs directly to your terminal with sanity functions logs

Automation with GROQ Delta Functions

What makes Sanity Functions particularly powerful is their deep integration with the content layer. Using special GROQ Delta Functions, your code can understand not just the current state of a document, but precisely what changed.

  • before() and after() give you snapshots of the document before and after the triggering event
  • delta::changedAny() and delta::changedOnly() let you check if specific fields were modified. Did the title change? Or only the title?
  • delta::operation() tells you if the document was created, updated, or deleted

You can build workflows that only run when a specific field is changed or when a document is first created, preventing unnecessary executions and complex boilerplate code.

Infrastructure as Code with Blueprints

A powerful engine needs a robust control system. Blueprints provide exactly that. They bring the discipline of infrastructure-as-code to your Sanity project, allowing you to define and manage your Functions (and other future Sanity resources) in a version-controlled file: sanity.blueprint.ts.

The workflow is clean and mirrors best practices from modern DevOps:

  1. Define: you declare your functions and their triggers in the blueprint file
  2. Plan: run ⁠sanity blueprints plan to see what changes will be made to your cloud environment
  3. Deploy: execute ⁠sanity blueprints deploy to push your functions to Sanity's infrastructure
  4. Manage: you can view info with sanity blueprints info or tear down resources with ⁠sanity blueprints destroy

This approach makes your automation setup repeatable, reviewable, and easy to manage as a team. And for a quick start, Sanity has provided a library of Recipes with pre-built patterns for common use cases.

Now we have the full picture: the AI brains with Agent Actions and the automation engine with Functions, all managed via Blueprints. Now, let’s put it all together and build something real.

From Theory to Practice

This is where the new toolkit moves from a list of features to a powerful, integrated system. At FocusReactive, we immediately used these tools to tackle a common content challenge: generating relevant, high-quality FAQ sections for our technical blog posts.

Here’s how we built it, showcasing how you can use Agent Actions both manually with a UI trigger and automatically in a hands-off workflow.

FAQ Schema

First things first, we need a home for our FAQ content. We'll set up a pretty standard schema with types for the FAQ section and its individual items:

// schemas/blog/postFaq.ts
export default {
name: 'postFaq',
title: 'Post FAQ',
type: 'object',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
validation: (Rule) => Rule.required(),
},
{
name: 'description',
title: 'Description',
type: 'array',
of: [{ type: 'block' }],
},
{
name: 'items',
title: 'FAQ Items',
type: 'array',
of: [{ type: 'postFaqItem' }],
validation: (Rule) => Rule.required().min(1),
},
],
};

// schemas/blog/postFaqItem.ts
export defaul {
name: 'postFaqItem',
title: 'Post FAQ Item',
type: 'object',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
validation: (Rule) => Rule.required(),
},
{
name: 'richText',
title: 'Text',
type: 'array',
of: [{ type: 'block' }],
validation: (Rule) => Rule.required(),
},
],
};

Custom Studio Action

First, we wanted to give editors manual control. What if they want to regenerate an FAQ for an already-published post? This is a perfect use case for calling an Agent Action directly from a custom Document Action in the Studio.

This demonstrates the power of the Agent Action API on its own - no serverless function needed.

We created a custom action that appears in the document's dropdown menu:

// actions/GenerateFaqAction.tsx
import { useState } from 'react';
import {
DocumentActionComponent,
DocumentActionDescription,
DocumentActionProps,
useClient,
} from 'sanity';
import { RobotIcon } from '@sanity/icons';

export const GenerateFaqAction: DocumentActionComponent = (
props: DocumentActionProps,
): DocumentActionDescription | null => {
const [isGenerating, setIsGenerating] = useState(false);
const agentClient = useClient({ apiVersion: 'vX' });

if (props.type !== 'post') {
return null;
}

const handleGenerateFaq = async () => {
setIsGenerating(true);

try {
const document = props.draft || props.published;

if (!document) {
alert('❌ No document data available');
return;
}

const { _id, title } = document;

console.log(`🔄 Regenerating FAQ for: ${title}`);

await agentClient.patch(_id).unset(['faq']).commit();

console.log(`🗑️ Existing FAQ cleared for: ${title}`);

const result = await agentClient.agent.action.generate({
schemaId: '_.schemas.default',
targetDocument: {
operation: 'edit',
_id,
},
// The instruction below is a simplified example. In a real-world application,
// you could provide much more detailed requirements, including rules for
// tone of voice, brand guidelines, or specific formatting for the output
instruction: `Generate 5-7 relevant FAQ items for this blog post about $blogTitle.
Create questions that readers would commonly ask about this technical topic.
Provide comprehensive, helpful answers based on the blog content ($blogContent) and description ($blogDescription).
Make the questions specific and actionable.
Focus on practical implementation details, common issues, and best practices.`,
instructionParams: {
blogTitle: { type: 'field' as const, path: 'title' },
blogContent: { type: 'field' as const, path: 'content' },
blogDescription: { type: 'field' as const, path: 'description' },
},
target: {
path: ['faq'],
},
});

console.log(`✅ FAQ regenerated successfully for: ${title}`);

props.onComplete();

alert(`✅ FAQ generated successfully for "${title}"!\\n\\nThe document has been updated.`);
} catch (error) {
console.error('❌ Error generating FAQ:', error);
alert(`❌ Error generating FAQ: ${error.message || 'Unknown error'}`);
} finally {
setIsGenerating(false);
}
};

return {
label: isGenerating ? 'Generating FAQ...' : 'Generate FAQ',
icon: RobotIcon,
disabled: isGenerating,
onHandle: handleGenerateFaq,
title: 'Generate FAQ section using AI based on the blog content',
};
};

With this in place, an editor can now click a button in the Studio, and the AI will generate and populate the FAQ field on demand.

Custom "Generate FAQ" document action in SanityCustom "Generate FAQ" document action in Sanity

Triggering on Publish

Now for the magic. We want to generate the FAQ automatically when a new post is published. This is where Functions and Blueprints come into play.

We first define our workflow in sanity.blueprint.ts. We're telling Sanity to run a function named generate-faq only on the publish event, and only for documents that are posts and don't already have an FAQ.

// sanity.blueprint.ts
import { defineBlueprint, defineDocumentFunction } from '@sanity/blueprints';

export default defineBlueprint({
resources: [
defineDocumentFunction({
name: 'generate-faq',
event: {
on: ['publish'],
filter: '_type == "post" && !defined(faq)',
projection: '{_id, title}',
},
}),
],
});

Next, we create the function itself. This code will look very similar to our custom action, but instead of being triggered by a user click, it's triggered by the publish event.

// functions/generate-faq/index.ts 
import { documentEventHandler } from '@sanity/functions';
import { createClient } from '@sanity/client';

export const handler = documentEventHandler(async ({ context, event }) => {
const client = createClient({
...context.clientOptions,
apiVersion: 'vX', // Required for Agent Actions
useCdn: false,
});

const { _id, title } = event.data;

console.log(`🚀 FAQ generation triggered for: ${_id} (${title})`);

if (!_id) {
console.error('❌ Missing _id in event data');
return;
}

try {
console.log(`🤖 Generating FAQ using AI for: ${title}`);

const result = await client.agent.action.generate({
schemaId: '_.schemas.fr-website',
targetDocument: {
operation: 'edit',
_id: `drafts.${_id}`, // Create/update draft version instead of published
},
instruction: `Generate 5-7 relevant FAQ items for this blog post about $blogTitle.
Create questions that readers would commonly ask about this technical topic.
Provide comprehensive, helpful answers based on the blog content ($blogContent) and description ($blogDescription).
Make the questions specific and actionable.
Focus on practical implementation details, common issues, and best practices.`,
instructionParams: {
blogTitle: { type: 'field' as const, path: 'title' },
blogContent: { type: 'field' as const, path: 'content' },
blogDescription: { type: 'field' as const, path: 'description' },
},
target: {
include: ['faq'],
},
noWrite: context.local, // Don't write when testing locally
});

console.log(`✅ Successfully generated FAQ for: ${title} (${result._id})`);
} catch (error) {
console.error(`❌ Error generating FAQ for ${title}:`, error);
throw error;
}
});

Deploy and Go

Finally, we deploy our automation with a single command:

sanity blueprints deploy

And now our workflow is complete. We have an on-demand, manual trigger inside the Studio and a fully automated, hands-off process for new content. This dual approach perfectly illustrates the flexibility of Sanity's new toolkit - you can meet editors where they are, while also building powerful, invisible automations.

Conclusion

The June 2025 update is more than just another set of features, it represents a fundamental shift in what we can expect from a content platform. At FocusReactive, we couldn't be more excited about this evolution.

We are already putting this knowledge into practice and are committed to using these capabilities to empower our clients. Our goal is to equip their content managers with the best possible tools, creating intuitive, AI-assisted workflows that automate repetitive tasks and unlock new creative potential.

A huge thank you to the entire Sanity team for their incredible vision and execution. It feels like every month they deliver something new and exciting that moves the entire industry forward. We can't wait to build with this and see what comes next.

To give you a concrete example of the output, you can see the AI-generated FAQ section for this article right below.

FAQ

This section answers common questions about Sanity's new automation and AI capabilities. It explores how these features compare to other services, their current limitations, and the practical benefits they offer to both developers and content teams.

CONTACT US TODAY

Don't want to fill out the form? Then contact us by email [email protected]