Back

Running a Vercel Cron Job Daily in SitecoreAI + Next.js Application

Thursday, September 3, 2026

Summary

When working with SitecoreAI + Next.js, there are often situations where you need to run a server-side script automatically — for example, syncing data, calling an API, cleaning up records, or running some custom business logic.

In a traditional Sitecore application, you might use a Sitecore scheduled task. With SitecoreAI or headless XM Cloud application, I prefer keeping this responsibility in the Next.js application and using Vercel Cron to trigger it.

Here’s a simple setup using the Next.js App Router.


1. Create a Route Handler

Create: src/app/api/cron/daily/route.ts

The route will contain the entry point for our scheduled job.

import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization');

  if (authHeader !== "Bearer $process.env.CRON_SECRET") {
    return NextResponse.json(
      { message: 'Unauthorized' },
      { status: 401 }
    );
  }

  try {
    await runDailyJob();

    return NextResponse.json({
      success: true,
      message: 'Daily job completed',
    });
  } catch (error) {
    console.error('Daily job failed:', error);

    return NextResponse.json(
      {
        success: false,
        message: 'Daily job failed',
      },
      { status: 500 }
    );
  }
}

async function runDailyJob() {
  // Your logic here

  console.log('Running daily job...');
}

The important part here is that the endpoint is protected with a secret. We don’t want anyone to be able to call our cron endpoint and trigger the job.

Add the secret to your environment variables:

CRON_SECRET=your-secret-value

Configure Vercel Cron

If your SitecoreAI + Next.js application is hosted on Vercel, add a vercel.json file to the project root:

{
  "crons": [
    {
      "path": "/api/cron/daily",
      "schedule": "0 0 * * *"
    }
  ]
}

The cron expression:

0 0 * * *

means every day at 12:00 AM.

One thing to keep in mind is the time zone. Make sure the cron schedule and your business requirement are aligned. If “12:00 AM” means midnight in a specific local time zone rather than UTC, account for that when configuring the schedule.

Keep the Job Logic Separate

As the job grows, I wouldn’t keep all the logic inside route.ts.

Instead, create something like:

src/
                                    ├── app/
                                    │   └── api/
                                    │       └── cron/
                                    │           └── daily/
                                    │               └── route.ts
                                    │
                                    └── services/
                                        └── daily-job.ts

Then:

// services/daily-job.ts

export async function runDailyJob() {
  console.log('Starting daily job');

  // Sitecore API calls
  // External API calls
  // Database operations
  // Other business logic

  console.log('Daily job completed');
}

And the /cron/daily/route.ts becomes very simple:


import { NextResponse } from 'next/server';
import { runDailyJob } from '@/services/daily-job';

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization');

  if (authHeader !== "Bearer $process.env.CRON_SECRET") {
    return NextResponse.json(
      { message: 'Unauthorized' },
      { status: 401 }
    );
  }

  try {
    await runDailyJob();

    return NextResponse.json({ success: true });
  } catch (error) {
    console.error('Daily job failed:', error);

    return NextResponse.json(
      { success: false },
      { status: 500 }
    );
  }
}

This keeps the cron endpoint responsible for triggering the job while the service contains the actual business logic.

Test It Locally

Before deploying, you can test the endpoint manually:

curl   -H "Authorization: Bearer your-secret-value"   http://localhost:3000/api/cron/daily


You should get:

{
  "success": true
}

The Final Setup

The overall flow is pretty simple:

        Vercel Cron
             │
             │ Every day at 12:00 AM
             ▼
      /api/cron/daily
             │
             ▼
       runDailyJob()
             │
             ├── Sitecore
             ├── External APIs
             ├── Database
             └── Other business logic

For a SitecoreAI + Next.js App Router application, this is a lightweight way to handle scheduled server-side work without introducing a separate cron server or coupling the job to the Sitecore platform.

For larger or long-running jobs, I’d consider pushing the actual work to a background queue rather than doing everything inside the HTTP request.

Running Cron Jobs in Lower Environments

Vercel Cron Jobs are automatically invoked only for Production deployments. Preview deployments and other lower environments do not receive scheduled Cron invocations.

If the Cron Job needs to run automatically in lower environments such as Development, QA, or Staging, an external scheduler can be used to invoke the same API endpoint.

For example, Windows Task Scheduler or Azure Logic Apps can periodically make an HTTP request to the lower-environment deployment:

                    
                                                  Windows Task Scheduler
                                                         |
                                                         │ Every day at 12:00 AM
                                                         ▼
                                                   https://qa.example.com/api/cron/daily
                                                         │
                                                         ▼
                                                      Cron Job

                             OR  

                                                  Azure Logic Apps
                                                         |
                                                         │ Scheduled HTTP request
                                                         ▼
                                                   https://qa.example.com/api/cron/daily
                                                         |
                                                         │
                                                         ▼
                                                      Cron Job

                                                      

Azure Logic Apps provides a Recurrence trigger and can make HTTP/HTTPS requests to external endpoints, making it a good option when the application is already using Azure.

The endpoint can be secured using the same CRON_SECRET pattern:

curl   -H "Authorization: Bearer $CRON_SECRET"   https://qa.example.com/api/cron/daily


Use a different CRON_SECRET for each environment so that the QA/Staging scheduler never has access to the Production secret. Vercel also recommends using CRON_SECRET to secure Cron Job invocations.

The resulting setup can be:

                    
        Local → Manual HTTP request
        Development → Windows Task Scheduler / Azure Logic Apps 
        QA → Windows Task Scheduler / Azure Logic Apps 
        Staging → Windows Task Scheduler / Azure Logic Apps 
        Production → Vercel Cron                                                 
            

This approach allows the same Cron API and business logic to be used across all environments, while the scheduling mechanism changes depending on the environment. Production uses Vercel Cron, while lower environments use an external scheduler.

Conclusion

Using Vercel Cron with a Next.js Route Handler gives us a simple and flexible way to run scheduled server-side tasks in a SitecoreAI + Next.js application.

Instead of relying on traditional Sitecore scheduling, the job lives with the Next.js application and can easily interact with Sitecore, external APIs, databases, or other services. It also keeps the implementation lightweight, easy to maintain, and independent of the Sitecore CM environment.

For SitecoreAI projects, this approach fits nicely with the headless and cloud-native architecture—Sitecore focuses on content management, while Next.js handles the application logic and scheduled workloads.

Overall, it gives us a clean separation of responsibilities and an easy way to add automation without introducing additional infrastructure.