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
Argument | Description |
---|---|
CMS_ID | ID of the CMS to read data from. |
CMS_MODEL_ID | ID of the model to read data from. |
Querystring Params | Required? | Description |
---|---|---|
q | No | URL encoded string of a JSON object for specifying the query parameters See more at Filter Query. Default filters include all records. |
q.limit | 100 | Fetch only up to this many records. |
q.offset | 0 | Skip this many records first. |
draft | No | Pass draft=1 to load draft/unpublished items. Requires secret token. |
locale | No | Locale tag for loading localized/translated fields (e.g. locale=ar-JO ). Locale tags are configured in your CMS’s settings. |
Header | Value |
---|---|
x-plasmic-api-cms-tokens | CMS_ID:CMS_PUBLIC_TOKEN |
Example: Load all items of a model
e.g. load all the testimonials
// 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 entriesconst 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:
{"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
)
// 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 Urlconst 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 onlylimit: 1,// Skip this many rows firstoffset: 0})}).toString();// Load filtered entriesconst 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:
{"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)
// ...apiUrl.search = new URLSearchParams({q: JSON.stringify({ where: { slug: 'my-first-blog-post' }, limit: 1 }),// Load the `Arabic (Jordan)` localized version of the blog postlocale: '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)
// ...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 postdraft: 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
// 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 entriesconst 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:
{"count": 3}
API: Create Item
Method: POST
https://data.plasmic.app/api/v1/cms/databases/
CMS_ID
/tables/
CMS_MODEL_ID
/rows
Argument | Description |
---|---|
CMS_ID | ID of the CMS to update. |
CMS_MODEL_ID | ID of the model to create. |
Header | Value |
---|---|
x-plasmic-api-cms-tokens | CMS_ID:CMS_SECRET_TOKEN |
content-type | application/json |
Example
e.g. create one or more blog posts
// 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 createconst firstItem = {identifier: 'First item',data: {author: 'First User',// message is localized, the empty string is the default localemessage: {'': '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 tablemessage: {'': 'Phasellus a massa fermentum, consequat orci at.'},// Setting imageUrl to null, it will remove the defaultimageUrl: null}};// Create entries, you can add `?publish=1` to the URL to automatically publish the created rowsconst 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:
{"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
Argument | Description |
---|---|
ROW_ID | ID of the row to publish. |
Header | Value |
---|---|
x-plasmic-api-cms-tokens | CMS_ID:CMS_SECRET_TOKEN |
Example
// 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 entryconst 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:
{"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
Argument | Description |
---|---|
ROW_ID | ID of the row to update. |
Header | Value |
---|---|
x-plasmic-api-cms-tokens | CMS_ID:CMS_SECRET_TOKEN |
content-type | application/json |
Example
// 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 updateconst updateItem = {identifier: 'Second item',data: {// Undefined fields won't be changed, null fields will be clearedmessage: {'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 rowconst 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:
{"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
Argument | Description |
---|---|
ROW_ID | ID of the row to delete. |
Header | Value |
---|---|
x-plasmic-api-cms-tokens | CMS_ID:CMS_SECRET_TOKEN |
Example
// 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 entryconst 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}`}});
Setting up a Preview link for Plasmic CMS
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.
Specifying the preview link
First, go to the model page, and click on the three-dot menu to access the CMS model settings modal.
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.
CMS models that have an associated preview link will have a Preview button displayed on the CMS entry page.
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:
<PlasmicRootProviderloader={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 inuseDraft: true
. - Be sure to do this in both your page rendering component, AND in
getStaticProps()
when you runextractPlasmicQueryData()
!
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:
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 vulnerabilityif (typeof req.query.path !== "string" || !req.query.path.startsWith("/")) {return res.status(401).json({ message: 'Invalid path' })}// Enable Draft Mode by setting the cookieres.setDraftMode({ enable: true })// Redirect to the pathres.redirect(req.query.path)}
And your catchall page can serve different content depending on if the user is in draft mode:
/*** 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-allreturn { props: {} };}const pageMeta = plasmicData.entryCompMetas[0];// Cache the necessary data fetched for the pageconst queryCache = await extractPlasmicQueryData(<PlasmicRootProviderloader={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 regenerationreturn { 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 (<PlasmicRootProviderloader={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.
{where: {<field>: <value>}}
Or an example using logical and conditional operators:
{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:
{"where": {"category": "articles"}} -
$in: Checks if a field’s value is in the specified array of values.
Example:
{"where": {"category": { "$in": ["news", "updates"] }}} -
$gt: Checks if a field’s value is greater than the specified value.
Example:
{"where": {"views": { "$gt": 1000 }}} -
$ge: Checks if a field’s value is greater than or equal to the specified value.
Example:
{"where": {"price": { "$ge": 20 }}} -
$lt: Checks if a field’s value is less than the specified value.
Example:
{"where": {"stock": { "$lt": 10 }}} -
$le: Checks if a field’s value is less than or equal to the specified value.
Example:
{"where": {"rating": { "$le": 4.5 }}} -
$regex: Matches the field’s value against the provided regular expression pattern.
Example:
{"where": {"title": { "$regex": ".*breaking.*" }}}
Combining Conditions with Logical Operators
-
$and: Use this operator to enforce that multiple conditions are all true.
Example:
{"where": {"$and": [{ "category": "news" },{ "views": { "$gt": 1000 } }]}} -
$or: Use this operator to specify that at least one of the conditions must be true.
Example:
{"where": {"$or": [{ "category": "news" },{ "category": "updates" }]}} -
$not: Use this operator to negate the condition.
Example:
{"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.
{"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.
{"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
- Go to the dashboard.
- Click on your workspace from the left sidebar.
- Click on your CMS.
- Click on the “CMS Settings” tab from the left sidebar.
- Find the “CMS ID”, “Public Token”, and “Secret Token”.
Find your model ID
- Go to the dashboard.
- Click on your workspace from the left sidebar.
- Click on your CMS.
- Click on the “Edit models” tab from the left sidebar.
- Click on your model.
- Find the “Unique identifier”.
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.
Have feedback on this page? Let us know on our forum.