Getting Started
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/123Endpoints
/api/pages/:id/cms-data/:collection
List records (public, paid content filtered)
/api/pages/:id/cms-data/:collection
Create a record (owner only)
/api/pages/:id/cms-data/:collection/:recordId
Get a single record
/api/pages/:id/cms-data/:collection/:recordId
Update a record (owner only)
/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
| Parameter | Type | Description |
|---|---|---|
where | JSON string | Filter by data fields, e.g., {"category":"news"} |
orderBy | string | Sort field: created_at or updated_at |
order | string | asc or desc (default) |
limit | number | Max records to return (default 50, max 100) |
offset | number | Number of records to skip for pagination |
before | number | Return records with ID less than this value (cursor pagination) |
after | number | Return 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/postsExample (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/postsPATCH /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-446655440000DELETE /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.
/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