Automate Sitecore Content Publishing with GraphQL, Next.js, and Vercel Cron Jobs

Learn how to utilize the Sitecore authoring API and Vercel to automate scheduled Sitecore content publishing.

May 21, 2025

By John Flores

Leveraging Sitecore's GraphQL API for Automated Content Delivery

The demand for efficient and flexible content management tools continues to grow in the digital space. While Sitecore is renowned for its powerful and adaptable platform, offering solutions for diverse scenarios, it lacks built-in functionality for automated, scheduled content publishing out-of-the-box. Manually publishing content, especially on a regular schedule, can be time-consuming and prone to errors.

Fortunately, Sitecore's inherent flexibility provides a powerful solution: the Sitecore Authoring and Management GraphQL API. This API gives developers granular control over content operations via a dedicated GraphQL endpoint.

This post focuses on harnessing this API to solve a common challenge: setting up automated daily publishing for specific Sitecore content items. We'll walk you through creating a Next.js serverless function that interacts with the Sitecore API and can be easily deployed and scheduled using Vercel Cron Jobs.

Why Automate Sitecore Publishing?

Implementing an automated publishing workflow offers significant advantages:

  • Reliable Scheduling: Ensure content goes live exactly when needed, meeting deadlines consistently without manual oversight.
  • Streamlined Operations: Free up your team from repetitive manual publishing tasks, reducing the chance of human error and increasing overall productivity.
  • Improved Consistency: Maintain a regular publishing cadence effortlessly.

The Automated Publishing Workflow

Our solution involves two core components:

  1. A Next.js Serverless API Route: This function will contain the logic to authenticate with Sitecore and trigger the publish operation via the GraphQL API.
  2. A Vercel Cron Job: This scheduler will trigger our Next.js API route at predefined intervals (e.g., daily).

The serverless function itself performs two main steps:

  1. Authentication: It obtains an access token from the Sitecore Identity service using your application's credentials.
  2. Publishing: It uses the obtained token to authenticate a GraphQL mutation request to the Sitecore Authoring and Management API, instructing Sitecore to publish the specified content.

Diagram showing the automation flow from authentication to GraphQL mutation to Vercel cron job

Diagram showing the flow: Vercel Cron Job triggers Next.js API route, which calls Sitecore Auth API for a token, then calls Sitecore GraphQL API to publish content.

Setting Up Sitecore XM Cloud Credentials

Before writing any code, we need to generate API credentials within the Sitecore XM Cloud Deploy Platform. These credentials (Client ID and Client Secret) are essential for authenticating our serverless function.

  1. Log in to the XM Cloud Deploy Platform.
  2. Navigate to Credentials in the left-hand navigation pane.
Sitecore XM Cloud Deploy interface with the 'Credentials' menu item highlighted in the sidebar
Screenshot of Sitecore XM Cloud Deploy Platform interface with the 'Credentials' menu item highlighted.
  1. If you don't have suitable credentials, click the Create credentials button on the top right.
Sitecore XM Cloud Deploy credentials screen with a red arrow pointing to the 'Create credentials' button
Screenshot highlighting the 'Create credentials' button in the XM Cloud Deploy Platform.
  1. A popup form will appear. Fill in the details to describe the purpose of these credentials (e.g., "Automated Publishing Service").
Popup form in XM Cloud Deploy for creating a new automation client with fields for label and description
Screenshot of the 'Create credentials' popup form in XM Cloud.
  1. After creation, Sitecore will display your Client ID and Client Secret. Crucially, the Client Secret is shown only once. Copy both values immediately and store them securely, typically as environment variables in your deployment environment (like Vercel).
Popup showing generated Client ID and one-time Client Secret in XM Cloud Deploy, with red arrows pointing to each field
Screenshot showing the generated Client ID and the one-time display of the Client Secret.

For our code example, we'll assume you've stored these in environment variables named SITECORE_CLIENT_ID and SITECORE_CLIENT_SECRET. You'll also need the URL for your Sitecore Authoring GraphQL endpoint, which we'll store as SITECORE_AUTHORING_ENDPOINT.

Building the Next.js API Route for Publishing

Now, let's create the Next.js API route (a serverless function) that handles the authentication and publishing logic. Create a file like pages/api/publish-content.ts:

import type { NextApiRequest, NextApiResponse } from 'next';

// Retrieve sensitive information and endpoints from environment variables
const CLIENT_ID = process.env.SITECORE_CLIENT_ID ?? '';
const CLIENT_SECRET = process.env.SITECORE_CLIENT_SECRET ?? '';
const GRAPHQL_ENDPOINT = process.env.SITECORE_AUTHORING_ENDPOINT ?? '';

// Static Sitecore Identity URL for token generation
const SITECORE_IDENTITY_URL = 'https://auth.sitecorecloud.io/oauth/token';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method Not Allowed' });
  }

  try {
    // Step 1: Get auth token
    const tokenRes = await fetch(SITECORE_IDENTITY_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        audience: 'https://api.sitecorecloud.io',
      }),
    });

    const tokenData = await tokenRes.json();
    if (!tokenRes.ok) throw new Error(tokenData.error_description || 'Failed to get token');

    const accessToken = tokenData.access_token;

    // Step 2: Publish item using GraphQL
    const graphqlQuery = {
      query: `
          mutation {
            publishItem(
              input: {
                languages: ["en"]
                rootItemPaths: [
                    "/sitecore/content/Blogs"
                ]
                targetDatabases: "experienceedge"
                publishItemMode: SMART
                publishRelatedItems: false
                publishSubItems: true
              }
            ) {
              operationId
            }
          }
        `,
    };

    const graphqlRes = await fetch(GRAPHQL_ENDPOINT, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(graphqlQuery),
    });

    const result = await graphqlRes.json();

    if (result.errors) {
      return res.status(500).json({ error: result.errors });
    }

    return res.status(200).json({ result: result.data.publishItem });
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: (error as Error).message || 'Unexpected error' });
  }
}

Understanding the Code

Let me breakdown the code above and give some explanation on each important section.

Setting Up the API Route

import type { NextApiRequest, NextApiResponse } from 'next';

const CLIENT_ID = process.env.SITECORE_CLIENT_ID ?? '';
const CLIENT_SECRET = process.env.SITECORE_CLIENT_SECRET ?? '';
const SITECORE_IDENTITY_URL = 'https://auth.sitecorecloud.io/oauth/token';
const GRAPHQL_ENDPOINT = process.env.SITECORE_AUTHORING_ENDPOINT ?? '';
  • We import Next.js types for API routes.
  • We set up environment variables for authentication.
  • We define the Sitecore authentication endpoint and GraphQL endpoint.

Creating the API Handler Function

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method Not Allowed' });
  }

  // Implementation will go here
}
  • We define an async function that handles API requests
  • We ensure only POST requests are accepted, returning a 405 error for other methods

Authentication with Sitecore

try {
  // Step 1: Get auth token
  const tokenRes = await fetch(SITECORE_IDENTITY_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      audience: 'https://api.sitecorecloud.io',
    }),
  });

  const tokenData = await tokenRes.json();
  if (!tokenRes.ok) throw new Error(tokenData.error_description || 'Failed to get token');

  const accessToken = tokenData.access_token;
  • We make a POST request to Sitecore's authentication endpoint.
  • We use the client credentials flow with our client ID and secret.
  • We parse the response to extract the access token.
  • We handle potential authentication errors.

Publishing Content with GraphQL

The Sitecore Authoring and Management GraphQL API provides a good amount of mutation options that will fit into different objectives. Checkout some of their examples here. In the query below we are using publishItem , which should function similarly to the publish function available on the editors.

  // Step 2: Publish item using GraphQL
  const graphqlQuery = {
    query: `
        mutation {
          publishItem(
            input: {
              languages: ["en"]
              rootItemPaths: [
                  "/sitecore/content/Blogs"
              ]
              targetDatabases: "experienceedge"
              publishItemMode: SMART
              publishRelatedItems: false
              publishSubItems: true
            }
          ) {
            operationId
          }
        }
      `,
  };

  const graphqlRes = await fetch(GRAPHQL_ENDPOINT, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(graphqlQuery),
  });

  const result = await graphqlRes.json();

Handling the Response

The rest of the code basically deals with what to do with response and handling any errors.

  if (result.errors) {
    return res.status(500).json({ error: result.errors });
  }

  return res.status(200).json({ result: result.data.publishItem });
} catch (error) {
  console.error(error);
  res.status(500).json({ error: (error as Error).message || 'Unexpected error' });
}

Scheduling with Vercel Cron Jobs

With the Next.js API route created and deployed, scheduling it is straightforward using Vercel Cron Jobs:

  1. Define the Cron Job: Add a crons configuration to your vercel.json file in the root of your project.
  2. Specify Path and Schedule: Point the path to your API route (e.g., /api/publish-content) and define the schedule using standard cron syntax.
// vercel.json
{
  "crons": [
    {
      "path": "/api/publish-content",
      "schedule": "0 8 * * *" // Runs every day at 8:00 AM UTC
    }
    // Add more cron jobs if needed
  ]
}

The schedule 0 8 * * * means the job runs at 0 minutes past the 8th hour, every day, every month, every day of the week (i.e., 8:00 AM UTC daily). Adjust the schedule to fit your requirements. You can use tools like crontab.guru to help generate cron expressions.

Deploy your Next.js application to Vercel. Vercel will automatically detect the crons configuration and schedule the job.

Streamlining Your Content Workflow with Automated Publishing

While Sitecore doesn’t offer built-in scheduled publishing, its powerful Authoring and Management GraphQL API provides the necessary tools for developers to implement this crucial functionality. By creating a simple Next.js serverless function and leveraging a scheduler like Vercel Cron Jobs, you can build a robust, automated publishing workflow. This not only saves valuable time and reduces manual errors but also ensures your content reaches your audience consistently and reliably, directly addressing a common pain point for content teams. This approach highlights the flexibility of Sitecore’s platform and the power of modern web development practices.

John Headshot

John Flores

Front-End Developer | Sitecore Technology MVP

John is a Senior Front-End Developer who is passionate about design and development. Outside of work, John has wide range of hobbies, from his plant collection, being a dog daddy, and a foodie. John is also a Sitecore Technology MVP!