Payload CMS SDK: CLI Toolkit for Faster Migrations
Set up an authenticated Payload CMS SDK client and run CLI scripts with tsx to speed migrations, content cleanup, and…

📚 Comprehensive Payload CMS Guides
Detailed Payload guides with field configuration examples, custom components, and workflow optimization tips to speed up your CMS development process.
During a recent migration from WordPress to Payload CMS, I ran into a familiar problem. Getting the data across was only half the work. The real time sink was all the small fixes afterward. Tweaking titles, correcting fields, cleaning up content. Doing all of that through the Admin UI quickly became slow and error prone.
Payload gives you several ways to input and modify data, but what helped me most during this phase was the SDK. Especially for targeted, one off changes, having a way to run authenticated scripts from the terminal was a huge productivity boost.
In this article, I will show how I set up a small SDK based toolkit for CLI scripts. It lets you run find, create, update, and delete operations directly against Payload without opening the Admin UI. This approach works particularly well during migrations, cleanups, and ongoing maintenance.
Step 1: Install dependencies and confirm the SDK is available
The toolkit relies on @payloadcms/sdk and runs scripts with tsx, so you need dependencies installed before anything else.
pnpm install
Once that completes, you’re ready to configure the SDK client used by all scripts in this folder.
Step 2: Configure the toolkit environment
The shared client loads credentials from a local .env.toolkit file so you can authenticate from the CLI.
# File: scripts/toolkit/.env.toolkit PAYLOAD_URL=http://localhost:80/api # OR set NEXT_PUBLIC_SERVER_URL and it will append /api # NEXT_PUBLIC_SERVER_URL=http://localhost:80 PAYLOAD_EMAIL=you@example.com PAYLOAD_PASSWORD=your-password
This file gives the SDK a base URL and login credentials. The client reads it automatically before it initializes, so every script can rely on the same configuration.
Step 3: Use the shared SDK client
All scripts should import the shared client from scripts/toolkit/client.ts. If you need authenticated access, call getAuthenticatedSdk() to get a JWT-backed SDK instance.
// File: scripts/toolkit/fix-titles.ts
import { getAuthenticatedSdk } from "./client";
async function run() {
const sdk = await getAuthenticatedSdk();
const pages = await sdk.find({ collection: "page", limit: 5 });
if (pages.docs[0]) {
await sdk.update({
collection: "page",
id: pages.docs[0].id,
data: { title: "Updated from CLI" },
});
}
}
run().catch(console.error);
This example logs in, queries a few pages, and updates one document. The key idea is that authentication happens once in getAuthenticatedSdk(), and the returned SDK carries the JWT for all subsequent requests.
Example: find, update, create, delete
Use the SDK for quick CRUD tasks while staying within Payload permissions.
import { getAuthenticatedSdk } from "./client";
async function run() {
const sdk = await getAuthenticatedSdk();
// Find
const pages = await sdk.find({ collection: "page", limit: 5 });
console.log("Found:", pages.totalDocs);
// Update
if (pages.docs[0]) {
await sdk.update({
collection: "page",
id: pages.docs[0].id,
data: { title: "Updated from CLI" },
});
}
// Create (remember tenant, see below)
await sdk.create({
collection: "page",
data: {
title: "CLI Page",
// tenant: 1,
},
});
// Delete
// await sdk.delete({ collection: 'page', id: pages.docs[0].id })
}
run().catch(console.error);
Step 4: Run the script from the terminal
With the script in place, run it using tsx so TypeScript executes without a build step.
npx tsx scripts/toolkit/fix-titles.ts
At this point you’re running real SDK operations from the CLI, with the same permissions your user has in Payload—just without the Admin UI.
Step 5: Multi-tenant note for this project
This repo supports multiple tenants (adart, making-light). When you create documents in tenant-aware collections, include the tenant field in data. Updates by id are safe because IDs are unique. If you need a tenant-aware example, review scripts/toolkit/example-bulk-update.ts.
Tenant-aware create example
import { getAuthenticatedSdk } from "./client";
async function run() {
const sdk = await getAuthenticatedSdk();
await sdk.create({
collection: "page",
data: {
title: "CLI Page (AdArt)",
tenant: 1,
},
});
}
run().catch(console.error);
Conclusion
The problem was simple but painful: CLI scripts were blocked by Admin-only workflows. By wiring a shared Payload SDK client and a local .env.toolkit, you can run authenticated operations directly from the terminal. You now know how to configure the SDK, write a script, and execute it in this repo without the Admin UI. Let me know in the comments if you have questions, and subscribe for more practical development guides.
Thanks, Matija