Workflows are the Document Authoring AI path for well-defined tasks. Your app selects a task, sends the document to the model, and receives structured edits to apply.
A workflow asks the model for focused edits. Your application can retry a failed attempt without adding a conversational tool loop.
Use workflows for tasks your app selects in advance. Common tasks include:
- Proofreading the document.
- Translating the document into another language.
- Replacing contract placeholders with field references.
- Flagging content that breaks your writing rules.
If the task isn’t known up front, or if it may need several turns, use agentic tools instead. Both paths share the same execution boundary, so you can ship them together.
How a workflow runs
A workflow uses three steps. Two steps run in the browser, and one step runs on your server.
- The browser reads the document and sends a snapshot to your server.
- The server prepares the model request, asks for focused edits, and applies them to the input snapshot with
run.apply(...). - The browser validates the output and applies it as one batch of edits.
The browser owns the editor, so it reads and writes the document. The server owns the model, so it stores prompts and API keys.
These examples require Document Authoring AI 2.0 and Document Authoring SDK 1.21.0 or later.
Built-in workflows
The toolkit ships two ready-to-use workflows.
Proofreading
Use the proofreading workflow to fix text errors without changing document intent:
const workflow = getBuiltInWorkflow("proofreading");Proofreading fixes spelling, grammar, punctuation, capitalization, duplicated words, and obvious typos.
It doesn’t rewrite for style, shorten the document, add content, delete content, or change formatting, so the diff stays predictable enough to apply as tracked changes.
Translation
Use the translation workflow to translate document text while preserving structure.
const workflow = getBuiltInWorkflow("translation", { targetLanguage: "spanish",});Translation supports english, german, french, and spanish, and it defaults to english if you omit targetLanguage.
The workflow preserves document structure, paragraph order, tables, names, numbers, dates, and formatting intent. Only the text changes.
Run a workflow in the browser
The following browser and server examples implement the proofreading workflow together.
Use the toolkit to read workflow input, call your server route, and apply the output.
import { getBuiltInWorkflow } from "@nutrient-sdk/document-authoring-ai";import { getAiToolkit } from "@nutrient-sdk/document-authoring-ai/editor";import type { DocAuthEditor } from "@nutrient-sdk/document-authoring";
declare const editor: DocAuthEditor;const toolkit = getAiToolkit(editor);const workflow = getBuiltInWorkflow("proofreading");const editorMode = editor.getEditorMode();
if (editorMode === "view") { throw new Error("Switch to Edit or Review mode before running this workflow.");}
const workflowInput = await toolkit.readWorkflowInput(workflow);
const response = await fetch("/api/ai/workflow", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ workflow: workflow.name, task: workflow.defaultTask, workflowInput, }),});
if (!response.ok) { throw new Error(`Workflow request failed: ${response.status}`);}const body: unknown = await response.json();if (typeof body !== "object" || body === null || !("output" in body)) { throw new Error("The workflow server returned no output.");}const { output } = body;
await toolkit.applyWorkflowOutput(workflow, output, { writeMode: editorMode === "review" ? "track_changes" : "apply", scope: workflowInput.scope,});readWorkflowInput returns a WorkflowInput with these values:
- Plain text content.
- A DocJSON fragment.
- The fragment contract.
- The scope, either
"selection"or"document".
Pass the full WorkflowInput to your server as the prompt input.
applyWorkflowOutput accepts the output assembled by the backend’s run.apply(...), which contains a replacementFragment. Do not pass raw model operations to the browser apply method.
applyWorkflowOutput applies the fragment through the browser SDK, which validates the fragment before it changes the document.
Then the toolkit replaces the selection or the whole document in one operation.
Call the model on the server
The server prepares the workflow, calls the model for focused edits, and assembles a replacement fragment from those edits.
The following example uses Vercel AI SDK generateText(opens in a new tab) with Output.object(opens in a new tab). The same pattern works with any library that supports JSON Schema or Zod-style output.
For framework-specific adapter details, refer to the Vercel AI SDK integration guide or the LangChain integration guide.
import { openai } from "@ai-sdk/openai";import { generateText, Output } from "ai";import { getBuiltInWorkflow, prepareWorkflowRun,} from "@nutrient-sdk/document-authoring-ai";import { toVercelAiWorkflowEditsSchema } from "@nutrient-sdk/document-authoring-ai/vercel";
export async function POST(req: Request) { try { const body: unknown = await req.json(); if (typeof body !== "object" || body === null || !("workflowInput" in body)) { return Response.json({ error: "workflowInput is required." }, { status: 400 }); } const workflow = getBuiltInWorkflow("proofreading"); const run = prepareWorkflowRun({ workflow, input: body.workflowInput });
const result = await generateText({ model: openai("gpt-5.4-mini"), system: run.systemPrompt, prompt: run.createPrompt(), output: Output.object(toVercelAiWorkflowEditsSchema()), });
return Response.json({ output: run.apply(result.output) }); } catch (error) { console.error("Proofreading workflow failed", error); return Response.json({ error: "The workflow could not be completed." }, { status: 500 }); }}Keep the same run for the model request and run.apply(...). The edits must target the input used to prepare that request.
run.apply(...) checks the operations and returns a WorkflowOutput. Pass an optional validateFragment function to prepareWorkflowRun(...) for backend fragment validation. The browser always validates the replacement before changing the document.
To retry, pass an error message to run.createPrompt(previousFailure) and call the model again. Your application controls retry limits. If browser fragment validation fails, WorkflowFragmentValidationError exposes code and issues; the document and selection remain unchanged.
Scope and Review mode
View mode is read-only. Check for View mode before you call applyWorkflowOutput.
For the full mode policy, refer to the review and approval guide.
Pass scope: workflowInput.scope to applyWorkflowOutput. This tells the toolkit whether to replace the selection or the whole document.
When scope is "document", the toolkit replaces the document directly, regardless of writeMode. It doesn’t use tracked changes for whole-document replacement.
applyWorkflowOutput doesn’t consume reviewComment. Review comments currently work only with agentic write tools.
Agentic write tools can require reviewer-facing motivation, but workflow output can’t.
Output shape
The model returns an object containing an operations array. The backend converts it with run.apply(...) into the following browser-facing type:
import type { WorkflowFragment } from "@nutrient-sdk/document-authoring-ai";
type WorkflowOutput = { replacementFragment: WorkflowFragment;};replacementFragment is a DocJSON fragment. It replaces the full input scope, either the selected content or the whole document body.
The model returns focused edits instead of rewriting the entire fragment. The backend assembles the replacement, and the browser SDK validates it before changing the document.
Custom workflows
If proofreading and translation don’t fit your task, write your own workflow:
import { createWorkflow } from "@nutrient-sdk/document-authoring-ai";
const workflow = createWorkflow({ name: "house_style_review", systemPrompt: `You are running the House Style Review workflow.
Review scope:- Make wording concise, direct, and consistent.- Preserve facts, document structure, formatting intent, and tone.- Do not add new claims or delete required content.- If the document has no issues that match this scope, return no operations.`, defaultTask: "Review the document against our writing rules without changing the facts.",});Pass custom workflows to prepareWorkflowRun(...) too. It adds the instructions for producing focused edits. Use its prompts and the same workflow edits schema.
If you need branching, planning, or arbitrary tool choice, you’ve outgrown workflows, so switch to agentic tools.