Plasmic CMS - API Reference

Overview

Plasmic CMS includes a RESTful HTTP API that allows you to create, read, update, and delete data in code; it enables integrating and using the data in Plasmic’s CMS with other platforms.

Authentication

The HTTP API can only be requested if you have authorization to access the requested CMS. The request for the endpoint must include a header x-plasmic-api-cms-tokens with a value of <CMS_ID>:<TOKEN>.

  • CMS_ID is the ID of the Plasmic CMS that you are accessing the data from.
  • TOKEN depends on which type of operation you’re making:
    • For read operations (e.g. Get Items), use your public token.
    • For write operations (e.g. Create Item), use your secret token.
      • WARNING: Anyone with your secret token can edit the content in your CMS. Only use your secret token in secure server environments, not in public websites.

Your CMS ID, Public Token, and Secret can be found on the “CMS Settings” tab. For step-by-step instructions, see Appendix.

API: Get Items

Method: GET

https://data.plasmic.app/api/v1/cms/databases/CMS_ID/tables/CMS_MODEL_ID/query

ArgumentDescription
CMS_IDID of the CMS to read data from.
CMS_MODEL_IDID of the model to read data from.
Querystring ParamsRequired?Description
qNoURL encoded string of a JSON object for specifying the query parameters See more at Filter Query. Default filters include all records.
q.limit100Fetch only up to this many records.
q.offset0Skip this many records first.
draftNoPass draft=1 to load draft/unpublished items. Requires secret token.
localeNoLocale tag for loading localized/translated fields (e.g. locale=ar-JO). Locale tags are configured in your CMS’s settings.
HeaderValue
x-plasmic-api-cms-tokensCMS_ID:CMS_PUBLIC_TOKEN

Example: Load all items of a model

e.g. load all the testimonials

Copy
// Find your CMS ID and Public Token from the settings page.
const CMS_ID = 'YOUR_CMS_ID';
const CMS_PUBLIC_TOKEN = 'YOUR_CMS_PUBLIC_TOKEN';
// Find your model's unique identifier from its model schema page.
const modelId = 'testimonials';
// Load all model entries
const response = await fetch(`https://data.plasmic.app/api/v1/cms/databases/${CMS_ID}/tables/${modelId}/query`, {
headers: {
// Your CMS ID and CMS Public API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_PUBLIC_TOKEN}`
}
});
const parsedResponse = await response.json();
console.log(parsedResponse);
const testimonials = parsedResponse.rows;
// ...

Response:

Copy
{
"rows": [
{
"id": "7vfFX4Ywen2fCBkzGH2J3x",
"createdAt": "2022-03-08T00:57:40.270Z",
"updatedAt": "2022-03-12T09:04:52.227Z",
"identifier": "First User",
"data": {
"author": "First User",
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"imageUrl": "https://picsum.photos/200/200?image=1"
}
},
{
"id": "nWsULzngbPha2RLtxAsYaW",
"createdAt": "2022-03-08T00:57:01.251Z",
"updatedAt": "2022-03-12T09:05:00.268Z",
"identifier": "Second User",
"data": {
"author": "Second User",
"message": "Phasellus a massa fermentum, consequat orci at.",
"imageUrl": "https://picsum.photos/200/200?image=2"
}
},
{
"id": "hN7Dw8h2FM6VRYJDcGsJyo",
"createdAt": "2022-03-08T00:57:20.789Z",
"updatedAt": "2022-04-15T17:48:24.101Z",
"identifier": "Third User",
"data": {
"author": "Third User",
"message": "Nam viverra dignissim arcu, eget ultrices elit aliquet.",
"imageUrl": "https://picsum.photos/200/200?image=3"
}
}
]
}

Example: Load a specific item

e.g. load a single blog post using its slug or its system ID (_id)

Copy
// Find your CMS ID and Public Token from the settings page.
const CMS_ID = 'YOUR_CMS_ID';
const CMS_PUBLIC_TOKEN = 'YOUR_CMS_PUBLIC_TOKEN';
// Find your model's unique identifier from its model schema page.
const modelId = 'blogPosts';
// Construct the API Url
const apiUrl = new Url(`https://data.plasmic.app/api/v1/cms/databases/${CMS_ID}/tables/${modelId}/query`);
// Set a filter to load entries with "slug" field value = "my-first-blog-post".
apiUrl.search = new URLSearchParams({
q: JSON.stringify({
where: {
slug: 'my-first-blog-post'
// Or to query by the system ID:
// _id: 'gN6Dw9h3FM5VRXJDcHsJz4'
// This only queries by system ID if there is no user-defined "_id" field.
},
// Load one item only
limit: 1,
// Skip this many rows first
offset: 0
})
}).toString();
// Load filtered entries
const response = await fetch(apiUrl.toString(), {
headers: {
// Your CMS ID and CMS Public API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_PUBLIC_TOKEN}`
}
});
const parsedResponse = await response.json();
const blogPost = parsedResponse.rows[0];
console.log(blogPost);

Response:

Copy
{
"id": "gN6Dw9h3FM5VRXJDcHsJz4",
"createdAt": "2022-04-15T17:38:24.101Z",
"updatedAt": "2022-04-15T17:48:24.101Z",
"identifier": "Blog Post 1",
"data": {
"author": "John Doe",
"title": "My First Blog Post",
"slug": "my-first-blog-post",
"body": "<h2>Lorem Ipsum</h2><p>Donec dignissim ornare magna, ac aliquet erat sodales eu.</p>"
}
}

Example: Load localized items

e.g. load a single blog post using its slug in Arabic (based on above example)

Copy
// ...
apiUrl.search = new URLSearchParams({
q: JSON.stringify({ where: { slug: 'my-first-blog-post' }, limit: 1 }),
// Load the `Arabic (Jordan)` localized version of the blog post
locale: 'ar-JO'
}).toString();
// ...

Example: Load draft versions of items

e.g. load the draft version of a single blog post using its slug (based on above example)

Copy
// ...
const useDraftVersion = true;
apiUrl.search = new URLSearchParams({
q: JSON.stringify({ where: { slug: 'my-first-blog-post' }, limit: 1 }),
// Load the Draft version of the blog post
draft: Number(useDraftVersion) // ?draft=1
}).toString();
// ...

API: Count Items

Method: GET

https://data.plasmic.app/api/v1/cms/databases/CMS_ID/tables/CMS_MODEL_ID/count

This takes the same arguments as the /query endpoint above, but returns just the count instead of returning row data.

Example: Count all items of a model

e.g. count all the testimonials

Copy
// Find your CMS ID and Public Token from the settings page.
const CMS_ID = 'YOUR_CMS_ID';
const CMS_PUBLIC_TOKEN = 'YOUR_CMS_PUBLIC_TOKEN';
// Find your model's unique identifier from its model schema page.
const modelId = 'testimonials';
// Load all model entries
const response = await fetch(`https://data.plasmic.app/api/v1/cms/databases/${CMS_ID}/tables/${modelId}/count`, {
headers: {
// Your CMS ID and CMS Public API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_PUBLIC_TOKEN}`
}
});
const parsedResponse = await response.json();
console.log(parsedResponse);
const testimonials = parsedResponse.rows;
// ...

Response:

Copy
{
"count": 3
}

API: Create Item

Method: POST

https://data.plasmic.app/api/v1/cms/databases/CMS_ID/tables/CMS_MODEL_ID/rows

ArgumentDescription
CMS_IDID of the CMS to update.
CMS_MODEL_IDID of the model to create.
HeaderValue
x-plasmic-api-cms-tokensCMS_ID:CMS_SECRET_TOKEN
content-typeapplication/json

Example

e.g. create one or more blog posts

Copy
// You can find your CMS ID and Secret Token from the settings page of your CMS.
const CMS_ID = 'YOUR_CMS_ID';
// WARNING: Anyone with your secret token can edit the content in your CMS.
// Only use your secret token in secure server environments, not in public websites.
const CMS_SECRET_TOKEN = 'YOUR_CMS_SECRET_TOKEN';
// Find your model's unique identifier from its model schema page.
const modelId = 'blogPosts';
// Construct the json for the itens you want to create
const firstItem = {
identifier: 'First item',
data: {
author: 'First User',
// message is localized, the empty string is the default locale
message: {
'': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'ar-JO': 'Nam viverra dignissim arcu, eget ultrices elit aliquet.'
},
imageUrl: 'https://picsum.photos/200/200?image=1'
}
};
const secondItem = {
data: {
// Not including a field or a locale will use the default values on that table
message: {
'': 'Phasellus a massa fermentum, consequat orci at.'
},
// Setting imageUrl to null, it will remove the default
imageUrl: null
}
};
// Create entries, you can add `?publish=1` to the URL to automatically publish the created rows
const response = await fetch(`https://data.plasmic.app/api/v1/cms/databases/${CMS_ID}/tables/${modelId}/rows`, {
method: 'POST',
headers: {
// Your CMS ID and CMS Secret API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_SECRET_TOKEN}`,
'content-type': 'application/json'
},
body: JSON.stringify({ rows: [firstItem, secondItem] })
});

Response:

Copy
{
"rows": [
{
"id": "fQukeSHQdijeTD22pPD3iZ",
"tableId": "37ZHFaa2oL6L3F6uEpSzCP",
"createdAt": "2022-05-18T16:32:13.164Z",
"updatedAt": "2022-05-18T16:32:13.164Z",
"identifier": "First item",
"data": null,
"draftData": {
"author": "First User",
"message": {
"": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"ar-JO": "Nam viverra dignissim arcu, eget ultrices elit aliquet."
},
"imageUrl": "https://picsum.photos/200/200?image=1"
}
},
{
"id": "387J2rZHouBSQhVdHbKDbY",
"tableId": "37ZHFaa2oL6L3F6uEpSzCP",
"createdAt": "2022-05-18T16:32:13.165Z",
"updatedAt": "2022-05-18T16:32:13.165Z",
"identifier": null,
"data": null,
"draftData": {
"author": "Anonymous Author",
"message": {
"": "Phasellus a massa fermentum, consequat orci at.",
"ar-JO": "Default Arabic (Jordan) localized message"
},
"imageUrl": null
}
}
]
}

API: Publish Item

Method: POST

https://data.plasmic.app/api/v1/cms/rows/ROW_ID/publish

ArgumentDescription
ROW_IDID of the row to publish.
HeaderValue
x-plasmic-api-cms-tokensCMS_ID:CMS_SECRET_TOKEN

Example

Copy
// Find your CMS ID and Secret Token from the settings page.
const CMS_ID = 'YOUR_CMS_ID';
// WARNING: Anyone with your secret token can edit the content in your CMS.
// Only use your secret token in secure server environments, not in public websites.
const CMS_SECRET_TOKEN = 'YOUR_CMS_SECRET_TOKEN';
const rowId = 'ROW_ID';
// Publish entry
const response = await fetch(`https://data.plasmic.app/api/v1/cms/rows/${rowId}/publish`, {
method: 'POST',
headers: {
// Your CMS ID and CMS Secret API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_SECRET_TOKEN}`
}
});

Response:

Copy
{
"id": "387J2rZHouBSQhVdHbKDbY",
"tableId": "37ZHFaa2oL6L3F6uEpSzCP",
"createdAt": "2022-05-18T16:32:13.165Z",
"updatedAt": "2022-05-18T16:32:13.165Z",
"identifier": null,
"data": {
"author": "Anonymous Author",
"message": {
"": "Phasellus a massa fermentum, consequat orci at.",
"ar-JO": "Default Arabic (Jordan) localized message"
},
"imageUrl": null
},
"draftData": null
}

API: Update Item

Method: PUT

https://data.plasmic.app/api/v1/cms/rows/ROW_ID

ArgumentDescription
ROW_IDID of the row to update.
HeaderValue
x-plasmic-api-cms-tokensCMS_ID:CMS_SECRET_TOKEN
content-typeapplication/json

Example

Copy
// Find your CMS ID and Secret Token from the settings page.
const CMS_ID = 'YOUR_CMS_ID';
// WARNING: Anyone with your secret token can edit the content in your CMS.
// Only use your secret token in secure server environments, not in public websites.
const CMS_SECRET_TOKEN = 'YOUR_CMS_SECRET_TOKEN';
const rowId = 'ROW_ID';
// Construct the json for item to update
const updateItem = {
identifier: 'Second item',
data: {
// Undefined fields won't be changed, null fields will be cleared
message: {
'ar-JO': null
},
imageUrl: 'https://picsum.photos/200/200?image=2'
}
};
// Update entry, you can add `?publish=1` to the URL to automatically publish the updated row
const response = await fetch(`https://data.plasmic.app/api/v1/cms/rows/${rowId}`, {
method: 'PUT',
headers: {
// Your CMS ID and CMS Secret API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_SECRET_TOKEN}`,
'content-type': 'application/json'
},
body: JSON.stringify(updateItem)
});

Response:

Copy
{
"id": "387J2rZHouBSQhVdHbKDbY",
"tableId": "37ZHFaa2oL6L3F6uEpSzCP",
"createdAt": "2022-05-18T16:32:13.165Z",
"updatedAt": "2022-05-18T16:51:14.336Z",
"identifier": "Second item",
"data": {
"author": "Anonymous Author",
"message": {
"": "Phasellus a massa fermentum, consequat orci at.",
"ar-JO": "Default Arabic (Jordan) localized message"
},
"imageUrl": null
},
"draftData": {
"author": "Anonymous Author",
"message": { "": "Phasellus a massa fermentum, consequat orci at.", "ar-JO": null },
"imageUrl": "https://picsum.photos/200/200?image=2"
}
}

API: Delete Item

Method: DELETE

https://data.plasmic.app/api/v1/cms/rows/ROW_ID

ArgumentDescription
ROW_IDID of the row to delete.
HeaderValue
x-plasmic-api-cms-tokensCMS_ID:CMS_SECRET_TOKEN

Example

Copy
// Find your CMS ID and Secret Token from the settings page.
const CMS_ID = 'YOUR_CMS_ID';
// WARNING: Anyone with your secret token can edit the content in your CMS.
// Only use your secret token in secure server environments, not in public websites.
const CMS_SECRET_TOKEN = 'YOUR_CMS_SECRET_TOKEN';
const rowId = 'ROW_ID';
// Delete entry
const response = await fetch(`https://data.plasmic.app/api/v1/cms/rows/${rowId}`, {
method: 'DELETE',
headers: {
// Your CMS ID and CMS Secret API token
'x-plasmic-api-cms-tokens': `${CMS_ID}:${CMS_SECRET_TOKEN}`
}
});

It is possible to associate a “preview link” for each CMS model. When editing a CMS entry, if the model has a “preview link”, then the entry form will have a “Preview” button that allows the content editor to see what the CMS entry will look like within the context of your real site. This especially makes sense for CMS models like blog posts, case studies, etc.

First, go to the model page, and click on the three-dot menu to access the CMS model settings modal.

Screenshot showing how to get to CMS modal settings

There, you can specify the preview url you’d like to use. In the url, you can use square brackets to reference fields from this CMS model; for example, https://mysite.com/blogs/[slug] will generate a preview link where the [slug] is replaced by the slug field value for the CMS entry you are looking at.

Screenshot showing how to get to CMS model settings for configuring preview url

CMS models that have an associated preview link will have a Preview button displayed on the CMS entry page.

Screenshot showing preview link for CMS entry

Setting up your server for preview

The preview link should point to your server that is capable of using the draft version of CMS data, instead of the default published version.

Configuring the CMS data fetcher to fetch draft data

To fetch draft CMS data, you need to pass in global contexts overrides for the CMS fetcher:

Copy
<PlasmicRootProvider
loader={PLASMIC}
globalContextsProps={{
cmsCredentialsProviderProps: {
databaseToken: process.env.PLASMIC_CMS_DRAFT_TOKEN,
useDraft: ["blogPost"]
}
}}
>

A few notes about the above:

  • The databaseToken must be the “secret” token, instead of the public token, for your CMS database. As such, it is important that you do not reveal this to the client. We recommend reading the token from an environment variable that will not be bundled and sent to the client, so that only your server is able to use the token to fetch data.
  • The useDraft prop can be used to specify exactly which CMS models should use draft data. If you want all CMS models to use draft data, you can pass in useDraft: true.
  • Be sure to do this in both your page rendering component, AND in getStaticProps() when you run extractPlasmicQueryData()!

Configuring your Next.js server to use draft data

It is up to you to decide how you want to enable viewing draft CMS data in your Next.js app, but we offer two possibilities.

A preview-only build

If you already have a stable link to some “preview” build of your application, you can simply repurpose that to power your CMS preview. This build will be hard-coded to use draft mode as described above.

Next.js draft mode

Next.js has a draft mode that sets a client-side cookie and serves “preview” content to users with that cookie. You can also choose to serve draft CMS data when the viewer is in draft mode.

You can follow along Next.js’s example on how to set up draft mode, but at a minimum, you will need some /api/draft endpoint like this:

Copy
import { NextApiRequest, NextApiResponse } from "next"
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.query.secret !== 'MY_SECRET_TOKEN') {
return res.status(401).json({ message: 'Invalid token' })
}
// Check that path is specified, and is a relative not absolute path, to
// avoid open redirect vulnerability
if (typeof req.query.path !== "string" || !req.query.path.startsWith("/")) {
return res.status(401).json({ message: 'Invalid path' })
}
// Enable Draft Mode by setting the cookie
res.setDraftMode({ enable: true })
// Redirect to the path
res.redirect(req.query.path)
}

And your catchall page can serve different content depending on if the user is in draft mode:

Copy
/**
* Used by both getStaticProps() and PlasmicLoaderPage component to turn on
* draft mode for Plasmic CMS
*/
function getGlobalContextProps(draftMode: boolean | undefined | null) {
if (draftMode) {
console.log("UsING DRAFT TOKEN", process.env.PLASMIC_CMS_DRAFT_TOKEN)
return {
cmsCredentialsProviderProps: {
databaseToken: process.env.PLASMIC_CMS_DRAFT_TOKEN,
useDraft: ["blogPost"]
}
};
} else {
return undefined;
}
}
export const getStaticProps: GetStaticProps = async (context) => {
const { catchall } = context.params ?? {};
const plasmicPath = typeof catchall === 'string' ? catchall : Array.isArray(catchall) ? `/${catchall.join('/')}` : '/';
const plasmicData = await PLASMIC.maybeFetchComponentData(plasmicPath);
if (!plasmicData) {
// non-Plasmic catch-all
return { props: {} };
}
const pageMeta = plasmicData.entryCompMetas[0];
// Cache the necessary data fetched for the page
const queryCache = await extractPlasmicQueryData(
<PlasmicRootProvider
loader={PLASMIC}
prefetchedData={plasmicData}
pageRoute={pageMeta.path}
pageParams={pageMeta.params}
globalContextsProps={getGlobalContextProps(context.draftMode)}
>
<PlasmicComponent component={pageMeta.displayName} />
</PlasmicRootProvider>
);
// Use revalidate if you want incremental static regeneration
return { props: { plasmicData, queryCache, draftMode: context.draftMode ?? null }, revalidate: 60 };
}
export default function PlasmicLoaderPage(props: {
plasmicData?: ComponentRenderData;
queryCache?: Record<string, any>;
draftMode?: boolean;
}) {
const { plasmicData, queryCache, draftMode } = props;
const router = useRouter();
if (!plasmicData || plasmicData.entryCompMetas.length === 0) {
return <Error statusCode={404} />;
}
const pageMeta = plasmicData.entryCompMetas[0];
return (
<PlasmicRootProvider
loader={PLASMIC}
prefetchedData={plasmicData}
prefetchedQueryData={queryCache}
pageRoute={pageMeta.path}
pageParams={pageMeta.params}
pageQuery={router.query}
globalContextsProps={getGlobalContextProps(draftMode)}
>
<PlasmicComponent component={pageMeta.displayName} />
</PlasmicRootProvider>
);
}

Filter Query

The where parameter inside q prop allows you to filter results based on specific conditions applied to fields in your data. The filters are constructed using various conditional operators, combined with logical operators like $and, $or, and $not.

Syntax

The filter query is a JSON object where the keys represent the field names, and the values represent the conditions you want to apply to those fields. You can also combine conditions using logical operators.

Copy
{
where: {
<field>: <value>
}
}

Or an example using logical and conditional operators:

Copy
{
where: {
<logical_operator>: {
<field>: {
<conditional_operator>: <value>
}
}
}
}

Logical Operators

  • $and: Combines multiple conditions where all must be true.
  • $or: Combines multiple conditions where at least one must be true.
  • $not: Negates the condition provided.

Conditional Operators

  • Primitive Values: You can directly compare a field to a primitive value (string, number, or boolean).

    Example:

    Copy
    {
    "where": {
    "category": "articles"
    }
    }
  • $in: Checks if a field’s value is in the specified array of values.

    Example:

    Copy
    {
    "where": {
    "category": { "$in": ["news", "updates"] }
    }
    }
  • $gt: Checks if a field’s value is greater than the specified value.

    Example:

    Copy
    {
    "where": {
    "views": { "$gt": 1000 }
    }
    }
  • $ge: Checks if a field’s value is greater than or equal to the specified value.

    Example:

    Copy
    {
    "where": {
    "price": { "$ge": 20 }
    }
    }
  • $lt: Checks if a field’s value is less than the specified value.

    Example:

    Copy
    {
    "where": {
    "stock": { "$lt": 10 }
    }
    }
  • $le: Checks if a field’s value is less than or equal to the specified value.

    Example:

    Copy
    {
    "where": {
    "rating": { "$le": 4.5 }
    }
    }
  • $regex: Matches the field’s value against the provided regular expression pattern.

    Example:

    Copy
    {
    "where": {
    "title": { "$regex": ".*breaking.*" }
    }
    }

Combining Conditions with Logical Operators

  • $and: Use this operator to enforce that multiple conditions are all true.

    Example:

    Copy
    {
    "where": {
    "$and": [
    { "category": "news" },
    { "views": { "$gt": 1000 } }
    ]
    }
    }
  • $or: Use this operator to specify that at least one of the conditions must be true.

    Example:

    Copy
    {
    "where": {
    "$or": [
    { "category": "news" },
    { "category": "updates" }
    ]
    }
    }
  • $not: Use this operator to negate the condition.

    Example:

    Copy
    {
    "where": {
    "$not": { "category": "news" }
    }
    }

Query Examples

Below are examples demonstrating how to chain logical operators ($and, $or, $not) to create complex filtering conditions in your queries.

Combining $and and $or Conditions

This example shows how to filter records where the category is either “news” or “blog” and the views are greater than 1000.

Copy
{
"where": {
"$and": [
{
"$or": [
{ "category": "news" },
{ "category": "blog" }
]
},
{ "views": { "$gt": 1000 } }
]
}
}

Multiple Levels of Nested Conditions

In this query we fetch records where the category is either “news” or “blog”, and records where the views are greater than 1000 or rating is higher than 4, but exclude records where the author is “John” or stock is below 5.

Copy
{
"where": {
"$and": [
{
"$or": [
{ "category": "news" },
{ "category": "blog" }
]
},
{
"$or": [
{ "views": { "$gt": 1000 } },
{ "rating": { "$gt": 4 } }
]
},
{
"$not": {
"$or": [
{ "author": "John" },
{ "stock": { "$lt": 5 } }
]
}
}
]
}
}

Appendix

Find your CMS IDs, Public Token, and Secret Token

  1. Go to the dashboard.
  2. Click on your workspace from the left sidebar.
  3. Click on your CMS.
  4. Click on the “CMS Settings” tab from the left sidebar.
  5. Find the “CMS ID”, “Public Token”, and “Secret Token”.
CMS settings

Find your model ID

  1. Go to the dashboard.
  2. Click on your workspace from the left sidebar.
  3. Click on your CMS.
  4. Click on the “Edit models” tab from the left sidebar.
  5. Click on your model.
  6. Find the “Unique identifier”.
CMS model editor

Find your row ID

Your row IDs will be returned in the response of all API calls. For example, see the response of API: Get Items.

Was this page helpful?

Have feedback on this page? Let us know on our forum.