Agent Actions: AI building blocks for structured content (ssr)
Written by Ken Jones
Missing Image!
Why Agent Actions matter
Ever wished AI could work directly with your structured content instead of operating in isolation? Agent Actions solve this problem by:
- Validating outputs against your content's structure
- Running from anywhere you can execute code
- Eliminating tedious reformatting work
As part of Sanity's Content Operating System, Agent Actions run directly in the Content Lake alongside your content, allowing you to trigger automation from Studio, Functions, Apps, your frontend, or anywhere your code lives. Use Agent Actions to adapt content to business needs across applications.
The Agent Actions toolkit
At the time of this writing, Sanity offers three specialized Agent Actions that work with your schema, not against it: Generate, Transform, and Translate.
Generate
When you need fresh content created that aligns with your schema, Generate is your go-to action. It creates new structured content based on instructions and context you provide.
const favoritePizza = "Detroit Style";
await client.agent.action.generate({
schemaId: "your-schema-id",
targetDocument: { operation: "create", _type: "menuItem" },
instruction: "Write a description for a $pizzaType pizza.",
instructionParams: {
pizzaType: {
type: "constant",
value: favoritePizza
}
}
});
With Generate, you can:
- Create full, structured documents with a single command
- Pull in context from existing documents, or even from external resources
- Generate images
- Connect references to existing documents
Transform
When you need to modify an existing document's content, Transform is what you want. It applies changes to documents while respecting the original structure and formatting.
await client.agent.action.transform({
schemaId: "your-schema-id",
documentId: "pizza-123",
instruction: "Rewrite this menu item to cater toward $audience.",
instructionParams: {
audience: 'An Italian pizza connoisseur'
}
});
Transform excels at:
- Changing document tone or style
- Standardizing content across your dataset
- Applying consistent terminology changes
Translate
When you need content in multiple languages, Translate makes the process seamless. It's a specialized version of Transform designed with internationalization in mind.
await client.agent.action.translate({
schemaId: "your-schema-id",
documentId: "pizza-123",
targetDocument: { operation: "create" },
fromLanguage: { id: "en-US", title: "English" },
toLanguage: { id: "it-IT", title: "Italian" },
styleGuide: "Translate with passion like an Italian pizzaiolo.",
protectedPhrases: ["Margherita", "Napoletana", "Quattro Formaggi"],
});
Translate supports:
- Document-level translation
- Field-level translation (coming soon)
- Style guides to maintain consistent voice
Getting started with Agent Actions
Let's walk through setting up and using Agent Actions in your project.
Prerequisites
Before we begin, you'll need:
@sanity/client
v7.1.0 or highersanity
CLI v3.88.0 or higher- A Sanity project with a schema
- A read/write API token
- Your
projectId
anddataset
name
Step 1: Deploy your schema
Agent Actions require a schemaId
to understand your content model. First, check if you've already deployed your schema:
sanity schema list
If you don't see your schema (or want to update it), deploy it:
# Deploy just the schema
sanity schema deploy
# Or deploy your entire studio
sanity deploy
Once deployed, run sanity schema list
again and copy the schema ID. It'll look something like _.schemas.default
.
Step 2: Configure the client
Create a file for your Agent Action code and set up the Sanity client:
import { createClient } from "@sanity/client";
const client = createClient({
projectId: "your-project-id",
dataset: "your-dataset",
apiVersion: "vX", // At the moment "vX" API version is required for Agent Actions, but this is just temporary until out of beta
token: "your-token"
});
Step 3: Create your first instruction
Now, let's write an instruction for Generate to create a new blog post:
await client.agent.action.generate({
schemaId: "your-schema-id",
targetDocument: {
operation: "create",
_type: "post"
},
instruction: "Write a blog post about $pizzaTopic with a catchy title and pizza metaphors.",
instructionParams: {
pizzaTopic: {
type: "constant",
value: "The History of Pizza"
}
}
});
This will create a new draft post with AI-generated content based on your instruction. The content will be structured according to your schema, so fields like title
, body
, and others will be populated and formatted appropriately.
Step 4: Trying Transform
Let's see how Transform differs from Generate:
await client.agent.action.transform({
schemaId: "your-schema-id",
documentId: "pizza-post-123",
instruction: "Replace 'New York style' with 'Detroit style'.",
target: [
path: ['title', 'body']
]
});
This instruction will update only the specified fields, keeping everything else exactly as it was.
Step 5: Translating content
And here's how you'd use Translate to create an Italian version of a post:
await client.agent.action.translate({
schemaId: "your-schema-id",
documentId: "pizza-post-123",
targetDocument: { operation: "create" },
fromLanguage: { id: "en-US", title: "English" },
toLanguage: { id: "it-IT", title: "Italian" },
styleGuide: "Translate with passion like an Italian pizzaiolo.",
protectedPhrases: ["Margherita", "Napoletana", "Quattro Formaggi"],
});
Real-world use cases
Here's how Agent Actions solve actual content challenges:
Content creation at scale
Imagine you have a product catalog with hundreds of items but minimal descriptions. You could use Generate to create draft descriptions based on product attributes:
// For each product without a description
const products = await client.fetch(`*[_type == "product" && !defined(description)]._id`);
for (const productId of products) {
await client.agent.action.generate({
schemaId: "your-schema-id",
documentId: productId,
instruction: `
Write a compelling product description based on $product.
Highlight key features and benefits in a persuasive way.
`,
instructionParams: {
product: {
type: "document",
documentId: productId
}
},
target: {
path: "description"
}
});
}
Automated translations
When expanding to new markets, you could set up a Sanity Function that automatically creates translated versions when a post is published:
// In a Sanity Function triggered on publish
export const handler = async ({ context, event }) => {
// Only translate posts
if (event.data._type !== 'post') return;
// Create translations for multiple languages
const languages = [
{ id: 'es-ES', title: 'Spanish' },
{ id: 'fr-FR', title: 'French' },
{ id: 'de-DE', title: 'German' }
];
for (const language of languages) {
await client.agent.action.translate({
schemaId: "your-schema-id",
documentId: event.data._id,
targetDocument: { operation: "create" },
fromLanguage: { id: "en-US", title: "English" },
toLanguage: language,
styleGuide: "Preserve tone and technical accuracy."
});
}
};
Custom Studio components
Create a button that generates content suggestions right in the Studio:
// GenerateButton.tsx
import {Button, Stack, TextArea, Text} from '@sanity/ui'
import {useClient, StringInputProps, useFormValue} from 'sanity'
export const GenerateButton = (props: StringInputProps) => {
const {value = '', elementProps} = props
const id = useFormValue(['_id']) as string // Access document values
const client = useClient({apiVersion: 'vX'}).withConfig({useCdn: false})
const handleGenerate = async (event: React.MouseEvent<HTMLButtonElement>) => {
try {
await client.agent.action.generate({
schemaId: '_.schemas.default',
documentId: id,
instruction: 'Suggest improvements to the "overview" to make it more engaging.',
target: {
path: 'aiSuggestions',
},
conditionalPaths: {
defaultReadOnly: false,
},
})
} catch {
console.log('Error running action')
}
}
return (
<Stack space={3}>
<TextArea rows={7} {...elementProps} value={typeof value === 'string' ? value : ''} />
<Button onClick={handleGenerate} text={'Get AI Suggestions'} />
</Stack>
)
}
// movie.schema.ts
// ... Other fields
defineField({
name: 'overview',
title: 'Overview',
type: 'blockContent',
group: 'main',
}),
defineField({
name: 'aiSuggestions',
title: 'AI Suggestions',
type: 'text',
group: 'main',
components: {
input: GenerateButton,
},
readOnly: () => true,
}),

Final thoughts
Agent Actions transform (pun intended) the way you think about automating your content creation workflows. No more hacking around AI outputs and wrestling with content that doesn't mesh with your content's schema.
Key advantages:
- Schema-aware outputs that fit your model
- Run from anywhere in your stack
- Field-level control with precise targeting for inclusion or exclusion
- Context-aware from existing content and validation
- Native integration with Sanity and part of the Sanity Client you already know and love.
Whether you're creating fresh content, standardizing existing assets, or expanding to new languages, Agent Actions help you put content at the core of your business.
Ready to start? Check out the complete documentation.
Join the conversation
What will you build with Agent Actions? We'd love to see your implementations. Join us on Discord to discuss your projects, ask questions, or share tips.