VISIMADE
← Developer Hub

Getting Started

  • Authentication
  • AI Coding Agents

Core APIs

Data APIs

  • Teams & Forking
  • Team Data API
  • Solo Data API
  • Social Data API
  • CMS Data API

Guides

Reference

CMS Data API

Manage creator-owned content collections for pages with CMS enabled. Only the page owner writes; visitors read.

Only the page owner can create, update, and delete records. All visitors can read free content. Paid content can be gated behind membership plans. Collection names must be lowercase alphanumeric with underscores (e.g., posts, products).

CMS vs Social Data: CmsData is creator-managed (only the page owner writes), while SocialData is community-managed (any authenticated user writes). Use CMS for blogs, product catalogs, FAQs, and other content managed by a single creator.

Images for CMS content: Use POST /api/pages/:id/assets/generate to create AI-generated images and store the returned publicUrl in your CMS records (e.g., as a featuredImage field). See Page Assets > Generate Image and the CMS Blog Example for the full workflow.


Setup

Enable CMS on a page by setting page_api_cms_enabled to true:

curl -X PATCH \
  -H "Authorization: Bearer vm_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"page_api_cms_enabled": true}' \
  https://visimade.com/api/pages/123

Endpoints
GET

/api/pages/:id/cms-data/:collection

List records (public, paid content filtered)

POST

/api/pages/:id/cms-data/:collection

Create a record (owner only)

GET

/api/pages/:id/cms-data/:collection/:recordId

Get a single record

PATCH

/api/pages/:id/cms-data/:collection/:recordId

Update a record (owner only)

DELETE

/api/pages/:id/cms-data/:collection/:recordId

Delete a record (owner only)


GET /api/pages/:id/cms-data/:collection

List records in a collection. Free records (planId: null) are returned to everyone. Paid records are only returned to the page owner or users with an active subscription to the associated plan. No authentication required for free content.

Query Parameters
ParameterTypeDescription
whereJSON stringFilter by data fields, e.g., {"category":"news"}
orderBystringSort field: created_at or updated_at
orderstringasc or desc (default)
limitnumberMax records to return (default 50, max 100)
offsetnumberNumber of records to skip for pagination
beforenumberReturn records with ID less than this value (cursor pagination)
afternumberReturn records with ID greater than this value (cursor pagination)
Example
curl "https://visimade.com/api/pages/123/cms-data/posts?limit=10&order=desc"
Response
{
  "records": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "collection": "posts",
      "data": { "title": "Hello World", "content": "<p>My first blog post</p>", "category": "news" },
      "planId": null,
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "total": 25, "limit": 10, "offset": 0, "hasMore": true
}
Cursor-Based Pagination

Use before and after instead of offset for stable pagination when records may be added or removed between requests. Pass a record ID as the cursor.

# Get the 20 most recent articles
curl "https://visimade.com/api/pages/123/cms-data/posts?limit=20"

# Load older articles (use the smallest ID from previous results)
curl "https://visimade.com/api/pages/123/cms-data/posts?limit=20&before=456"

# Poll for new articles since your last fetch
curl "https://visimade.com/api/pages/123/cms-data/posts?after=500&order=asc"

The response includes hasMore: true/false to indicate if more records exist beyond the current page. When using cursors, offset is ignored.


POST /api/pages/:id/cms-data/:collection

Create a new record. Only the page owner can create CMS records. Requires cms-data:write scope. Optionally attach a planId to gate the record behind a membership plan.

Example (free content)
curl -X POST \
  -H "Authorization: Bearer vm_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"data": {"title": "New post", "content": "Hello world!"}}' \
  https://visimade.com/api/pages/123/cms-data/posts
Example (paid content)
curl -X POST \
  -H "Authorization: Bearer vm_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"data": {"title": "Premium guide"}, "planId": 42}' \
  https://visimade.com/api/pages/123/cms-data/posts

PATCH /api/pages/:id/cms-data/:collection/:recordId

Update an existing record. Data is merged (not replaced). Only the page owner can update. Requires cms-data:write scope. You can also change the planId to gate or ungate content.

Example
curl -X PATCH \
  -H "Authorization: Bearer vm_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"data": {"title": "Updated title"}}' \
  https://visimade.com/api/pages/123/cms-data/posts/550e8400-e29b-41d4-a716-446655440000

DELETE /api/pages/:id/cms-data/:collection/:recordId

Delete a record. Only the page owner can delete. Requires cms-data:write scope. Returns 204 on success, 404 if not found.

curl -X DELETE \
  -H "Authorization: Bearer vm_your_token_here" \
  https://visimade.com/api/pages/123/cms-data/posts/550e8400-e29b-41d4-a716-446655440000

Batch Operations

Execute up to 100 create, update, and delete operations in a single request. The entire batch runs in one database transaction (all-or-nothing) and counts as a single rate limit hit. Only the page owner can perform batch operations.

POST

/api/pages/:id/cms-data/:collection/batch

Execute batch operations (owner only)

Request Body
{
  "operations": [
    { "op": "create", "data": { "title": "New post" }, "planId": null },
    { "op": "update", "id": "uuid-1", "data": { "title": "Updated" } },
    { "op": "delete", "id": "uuid-2" }
  ]
}
Response
{
  "results": [
    { "op": "create", "success": true, "id": "uuid-new", "data": { "title": "New post" }, "planId": null },
    { "op": "update", "success": true, "id": "uuid-1" },
    { "op": "delete", "success": true, "id": "uuid-2" }
  ]
}
Update = Shallow Merge (same as PATCH)

The update operation merges the provided data with the existing record's data. Only the fields you include are overwritten — fields you omit are preserved. For example, if a record has {"title": "Hello", "views": 10} and you send{"data": {"views": 11}}, the result is {"title": "Hello", "views": 11}. However, if you send a field with null, it will be set to null. This is a shallow merge — nested objects are fully replaced, not deep-merged.

Rules: Maximum 100 operations per batch. All operations run atomically. Schema validation is applied to each create/update. Requires cms-data:write scope. Create operations support optional planId to gate content behind a membership plan.

On this page

  • Setup
  • Endpoints
  • GET - List Records
  • POST - Create Record
  • PATCH - Update Record
  • DELETE - Delete Record
  • Batch Operations