How to Update Sitecore XM Cloud Fields from a Next.js Application Using the Authoring API
Learn how to integrate Sitecore XM Cloud's Authoring API with your Next.js application to programmatically update content fields and workflows in real-time.
Start typing to search...
Learn how to integrate Sitecore XM Cloud's Authoring API with your Next.js application to programmatically update content fields and workflows in real-time.
When building headless applications with Sitecore XM Cloud, you often need to update content fields programmatically — for workflows, approvals, or dynamic data sync.
This guide shows you how to implement field updates from a Next.js application using GraphQL mutations against the Authoring API.
Typical use cases:
Before starting, ensure you have:
| Endpoint | Purpose | Notes |
|---|---|---|
/sitecore/api/graph/edge |
Read-only queries for rendering | Use in frontend apps |
/sitecore/api/authoring/graphql/v1 |
Mutations & item updates | Use for updating fields |
/api/v1/pages |
Page-level operations only | Not for regular items |
To update fields, always use the Authoring endpoint:
https://your-instance.sitecorecloud.io/sitecore/api/authoring/graphql/v1
Click “Create credentials” and select “Automation”

Configure: • Label: “API Integration” • Location: “Edge administration”
You’ll receive:
CLIENT_ID=your_client_id_here
CLIENT_SECRET=your_client_secret_here
Add to your .env.local:
SITECORE_CLIENT_ID=your_client_id_here
SITECORE_CLIENT_SECRET=your_client_secret_here
SITECORE_AUTHORING_ENDPOINT=https://your-instance.sitecorecloud.io/sitecore/api/authoring/graphql/v1
SITECORE_AUTH_ENDPOINT=https://auth.sitecorecloud.io/oauth/token
SITECORE_AUDIENCE=https://api.sitecorecloud.io
Important: Use the authoring endpoint for mutations, not the edge endpoint.
Create lib/sitecore-update.ts:
This service is a reusable helper class that wraps the Authoring API mutation logic. It keeps your GraphQL update queries in one place so you can call them from anywhere in your app.
interface UpdateResult {
success: boolean;
message?: string;
error?: string;
}
class SitecoreUpdateService {
private authoringEndpoint: string;
constructor() {
this.authoringEndpoint = process.env.SITECORE_AUTHORING_ENDPOINT || "";
}
/**
* Update a single field value
*/
async updateField(
itemId: string,
fieldName: string,
fieldValue: string,
accessToken: string,
language: string = "en"
): Promise<UpdateResult> {
try {
const mutation = `
mutation UpdateField($id: ID!, $lang: String!, $fieldName: String!, $value: String!) {
updateItem(input: {
itemId: $id,
language: $lang,
fields: [{ name: $fieldName, value: $value }]
}) {
item {
name
path
}
}
}
`;
const variables = {
id: itemId,
lang: language,
fieldName: fieldName,
value: fieldValue,
};
const response = await fetch(this.authoringEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
query: mutation,
variables,
operationName: "UpdateField",
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.errors) {
throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
}
return {
success: true,
message: `Field "${fieldName}" updated successfully`,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* Update multiple fields in one request
*/
async updateMultipleFields(
itemId: string,
fields: Array<{ name: string; value: string }>,
accessToken: string,
language: string = "en"
): Promise<UpdateResult> {
try {
const mutation = `
mutation UpdateMultipleFields($id: ID!, $lang: String!, $fields: [ItemFieldInput!]!) {
updateItem(input: {
itemId: $id,
language: $lang,
fields: $fields
}) {
item {
name
path
}
}
}
`;
const variables = {
id: itemId,
lang: language,
fields: fields,
};
const response = await fetch(this.authoringEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
query: mutation,
variables,
operationName: "UpdateMultipleFields",
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.errors) {
throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
}
return {
success: true,
message: `Successfully updated ${fields.length} fields`,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
}
export const sitecoreUpdateService = new SitecoreUpdateService();
Create pages/api/sitecore/update-field.ts:
This API route acts as a secure server-side proxy between your Next.js frontend and Sitecore. It ensures credentials stay hidden and handles requests from your React components.
import { NextApiRequest, NextApiResponse } from "next";
import { sitecoreUpdateService } from "../../../lib/sitecore-update";
import { getAccessToken } from "../../../lib/auth"; // Your auth implementation
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
try {
const { itemId, fieldName, fieldValue, language = "en" } = req.body;
// Validate required parameters
if (!itemId || !fieldName || fieldValue === undefined) {
return res.status(400).json({
error: "Missing required parameters",
required: ["itemId", "fieldName", "fieldValue"],
});
}
// Get access token (implement this based on your auth setup)
const accessToken = await getAccessToken();
// Update the field
const result = await sitecoreUpdateService.updateField(
itemId,
fieldName,
fieldValue,
accessToken,
language
);
if (result.success) {
return res.status(200).json({
success: true,
message: result.message,
data: { itemId, fieldName, fieldValue, language },
});
} else {
return res.status(500).json({
success: false,
error: result.error,
});
}
} catch (error) {
console.error("Update field error:", error);
return res.status(500).json({
success: false,
error: "Internal server error",
});
}
}
Create hooks/use-sitecore-field-update.ts:
This custom React hook provides a simple interface for UI components to trigger Sitecore field updates. It handles loading state and abstracts the API call so your components stay clean.
import { useState } from "react";
interface UpdateParams {
itemId: string;
fieldName: string;
fieldValue: string;
language?: string;
}
interface UpdateResult {
success: boolean;
message?: string;
error?: string;
}
export const useSitecoreFieldUpdate = () => {
const [isLoading, setIsLoading] = useState(false);
const [lastResult, setLastResult] = useState<UpdateResult | null>(null);
const updateField = async (params: UpdateParams): Promise<UpdateResult> => {
setIsLoading(true);
setLastResult(null);
try {
const response = await fetch("/api/sitecore/update-field", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
const result: UpdateResult = await response.json();
setLastResult(result);
return result;
} catch (error) {
const errorResult: UpdateResult = {
success: false,
error: error instanceof Error ? error.message : "Network error",
};
setLastResult(errorResult);
return errorResult;
} finally {
setIsLoading(false);
}
};
const updateMultipleFields = async (
itemId: string,
fields: Array<{ name: string; value: string }>,
language?: string
): Promise<UpdateResult> => {
setIsLoading(true);
setLastResult(null);
try {
const response = await fetch("/api/sitecore/update-multiple-fields", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ itemId, fields, language }),
});
const result: UpdateResult = await response.json();
setLastResult(result);
return result;
} catch (error) {
const errorResult: UpdateResult = {
success: false,
error: error instanceof Error ? error.message : "Network error",
};
setLastResult(errorResult);
return errorResult;
} finally {
setIsLoading(false);
}
};
return {
updateField,
updateMultipleFields,
isLoading,
lastResult,
};
};
// Update approval status field
const updateApprovalStatus = async (itemId: string, status: string) => {
const result = await updateField({
itemId: itemId,
fieldName: "approvalStatus",
fieldValue: status, // "Pending", "In Progress", "Approved"
});
if (result.success) {
console.log("Status updated successfully");
} else {
console.error("Update failed:", result.error);
}
};
// Update multiple fields in a single request
const updateContentFields = async (itemId: string) => {
const fields = [
{ name: "title", value: "New Title" },
{ name: "description", value: "Updated description" },
{ name: "lastModified", value: new Date().toISOString() },
];
const result = await updateMultipleFields(itemId, fields);
if (result.success) {
console.log(`Updated ${fields.length} fields`);
}
};
export default function SitecoreStatusTest() {
const { updateField } = useSitecoreFieldUpdate();
const [status, setStatus] = useState("Pending");
return (
<button
onClick={() => updateField("8F128029CDC84C6DB31D9A9AE09A91E9", "approvalItemStatus", "Approved")}
>
Approve
</button>
);
}
Schema is not configured for mutations → You’re calling the Edge endpoint. Use /authoring/graphql/v1.Variable "$id" of type "String!" … expecting "ID!" → Change mutation variable to $id: ID!.audience.Validation helpers:
const validateItemId = (id: string) => /^[0-9A-F-]{36}$/i.test(id);
updateMultipleFields instead of calling updateField repeatedly.The Sitecore XM Cloud Authoring API is the backbone for enabling write operations in a headless setup. While the Edge GraphQL endpoint powers fast read-only queries for front-end rendering, the Authoring API lets developers go further — updating fields, triggering workflow changes, and synchronizing external systems.
By integrating it into your Next.js application:
The key lesson: always use the Authoring API for mutations, Edge for queries, and never expose secrets to the client side. With a solid service layer and clear error handling, your Next.js apps can confidently manage Sitecore content in real time.
This opens up endless possibilities for building custom admin panels, workflow tools, or real-time integrations — all while staying headless and future-proof.