Agent Guide
Everything an AI agent needs to build and edit pages on Visimade. This is the single reference — read this page and you're ready to go.
Platform Overview
Visimade is a platform for creating interactive web pages with built-in data storage. Each page is a single HTML file with inline CSS and JavaScript. Pages can store user data in the cloud using one of several storage modes. Client-side JavaScript SDKs (SoloData, TeamData, SocialData, CmsData, VisiForm) are automatically injected — you never include script tags for them.
Authentication
All API requests require a Bearer token:
Authorization: Bearer vm_your_token_here
Your token and page ID are provided in your hidden prompt. If a token stops working, the user may send a new one in chat — always use the most recent token.
Page Management
Read a Page
GET /api/pages/:id
→ { page: { id, name, slug, html_content, is_published, ... }, storageMode }Update a Page
PATCH /api/pages/:id
Body: { "html_content": "<!DOCTYPE html>...", "name": "New Title" }
# All fields optional. Creates a version backup automatically.Lookup by Slug
GET /api/pages/lookup?slug=page-slug
→ { id, name, slug, isPublished }HTML Rules
Must be a complete HTML document with
<!DOCTYPE html>Add
data-page-id="{{PAGE_ID}}"to the<body>tag — required for all data SDKs to work{{PAGE_ID}}is replaced with the actual page ID at serve timeSingle HTML file with inline CSS and JavaScript — no external files
Do NOT include
<script>tags for SoloData, TeamData, SocialData, CmsData, or VisiForm — they are auto-injectedMake pages responsive and mobile-friendly
Include error handling in JavaScript
Sending HTML via API
Write HTML to a local file, then use curl with jq to send it:
# Write HTML to file
cat > page.html << 'HTMLEOF'
<!DOCTYPE html>
<html>
<head><title>My Page</title></head>
<body data-page-id="{{PAGE_ID}}">
<h1>Hello World</h1>
</body>
</html>
HTMLEOF
# PATCH it to the API
curl -X PATCH "$BASE_URL/api/pages/$PAGE_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html page.html '{html_content: $html}')"
# For large pages, build incrementally:
# 1. Start with structure and basic content, PATCH it
# 2. Add more sections, PATCH again
# NEVER try to generate the entire HTML in a single outputStorage Modes
Set via PATCH /api/pages/:id with { "storage_mode": "team_app" }, or pre-configured in your hidden prompt.
page — No cloud storage. Use localStorage or in-memory state.
solo_app — Private per-user data. Each user sees only their own records. SDK:
window.SoloDatateam_app — Shared team data with roles (owner/admin/member/viewer). SDK:
window.TeamDatasocial_app — Public data where users own their records. Everyone can read, only creators can edit/delete their own. SDK:
window.SocialData
When to use CMS mode: For blogs, catalogs, FAQs, or any content managed by the page owner and read by visitors, enable page_api_cms and use window.CmsData. CMS works alongside any storage mode.
When to use Form mode: For contact forms, waitlists, reservations, RSVPs, or any anonymous submissions, enable page_api_form and use window.VisiForm. Visitors submit without logging in; only the page owner can read submissions. Form works alongside any storage mode.
Client-Side SDKs
These are automatically injected based on storage mode. Always await SDK.ready before using.
SoloData (solo_app)
await SoloData.ready;
await SoloData.create('collection', { key: 'value' });
const { records } = await SoloData.find('collection');
await SoloData.update('collection', recordId, { key: 'newValue' });
await SoloData.delete('collection', recordId);
SoloData.getCurrentUser(); // { id, username } or null
SoloData.promptLogin(); // Open login modalTeamData (team_app)
await TeamData.ready;
await TeamData.create('collection', { key: 'value' });
const { records } = await TeamData.find('collection', { where: { status: 'active' } });
await TeamData.findMine('collection');
await TeamData.update('collection', recordId, { key: 'newValue' });
await TeamData.delete('collection', recordId);
TeamData.isMember(); // Is team member
TeamData.getRole(); // 'owner' | 'admin' | 'member' | 'viewer'
TeamData.canEdit(record); // Check permissionSocialData (social_app)
await SocialData.ready;
await SocialData.create('collection', { key: 'value' });
const { records } = await SocialData.find('collection'); // All public records
await SocialData.findMine('collection'); // Only user's records
await SocialData.update('collection', recordId, { key: 'newValue' }); // Owner only
await SocialData.delete('collection', recordId); // Owner only
SocialData.getCurrentUser();
SocialData.isAuthenticated();Query options (all SDKs):
await SDK.find('collection', {
where: { status: 'active', category: 'news' }, // Filter
orderBy: 'created_at', // Sort field
order: 'desc', // 'asc' or 'desc'
limit: 50, // Max records (default 50, max 100)
offset: 0 // Pagination
});CMS Data
Creator-managed content (one writer, many readers). Enable via page settings or page_api_cms. Only the page owner can create/update/delete. All visitors can read.
await CmsData.ready;
// Reading (everyone)
const { records } = await CmsData.find('posts', {
where: { category: 'news' },
orderBy: 'created_at',
order: 'desc',
limit: 10
});
const post = await CmsData.findById('posts', recordId);
// Writing (creator only)
await CmsData.create('posts', { title: 'Hello', content: '<p>World</p>' });
await CmsData.update('posts', recordId, { title: 'Updated' });
await CmsData.delete('posts', recordId);
// Identity
CmsData.isCreator(); // true if page owner
CmsData.isAuthenticated(); // true if logged in
CmsData.getCurrentUser(); // { id, username } or null
CmsData.promptLogin(); // open login modalForm Data (VisiForm)
Anonymous form submissions (anyone writes, only owner reads). Enable via page settings or page_api_form. Visitors submit without logging in. Only the page owner can view/manage submissions.
await VisiForm.ready;
// Submitting (anyone - no login required)
await VisiForm.submit('waitlist', { email: 'user@example.com', name: 'Jane' });
await VisiForm.submit('inquiries', { message: 'Hello!', email: 'visitor@example.com' });
// Reading submissions (page owner only)
const { records, total } = await VisiForm.find('waitlist', {
orderBy: 'created_at',
order: 'desc',
limit: 50
});
const record = await VisiForm.findOne('waitlist', recordId);
// Managing submissions (page owner only)
await VisiForm.update('waitlist', recordId, { status: 'approved' });
await VisiForm.delete('waitlist', recordId);
// Identity
VisiForm.isOwner(); // true if page owner
VisiForm.isAuthenticated(); // true if logged inPage Assets
Manage images and files that belong to a page. Max 1000 assets per page.
Server-Side (API)
# List assets (public, no auth)
GET /api/pages/:id/assets
# Upload file (owner only)
POST /api/pages/:id/assets
Content-Type: multipart/form-data
curl -F "file=@image.png" -H "Authorization: Bearer $TOKEN" $BASE_URL/api/pages/$PAGE_ID/assets
# Generate AI image (owner only, costs 1 credit)
POST /api/pages/:id/assets/generate
Body: { "prompt": "A mountain landscape", "filename": "mountain.png", "aspect_ratio": "16:9" }
# aspect_ratio options: "1:1", "16:9", "9:16", "4:3", "3:4"
# Delete asset (owner only)
DELETE /api/pages/:id/assets/:assetIdClient-Side (JavaScript SDK)
// List all assets (always available, no auth needed)
const { assets } = await PageAssets.list();
// assets: [{ id, filename, content_type, size, url, created_at }]
// Get by filename
const asset = await PageAssets.getByFilename('logo.png');
// Get URL directly
const logoUrl = await PageAssets.getUrl('logo.png');
// Upload (owner only)
const newAsset = await PageAssets.upload(fileInput.files[0]);
// Delete (owner only)
await PageAssets.delete(assetId);
// Check ownership
PageAssets.isOwner();Page AI Features
These must be enabled in page settings. They are available as client-side JavaScript APIs for visitors to use.
// AI Chat (page_api_ai) — costs credits
const response = await PageAI.chat("Summarize this text...");
// AI Image Generation (page_api_image_gen) — costs 1 credit
const imageUrl = await PageAI.generateImage("A sunset over mountains");
// Image Search (page_api_image_search) — free, rate limited
const images = await PageAI.searchImages("company logo");Starter Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Title</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.6;
color: #1e293b;
background: #f8fafc;
}
.container { max-width: 900px; margin: 0 auto; padding: 40px 20px; }
</style>
</head>
<body data-page-id="{{PAGE_ID}}">
<div class="container">
<h1>Hello World</h1>
</div>
<script>
// Example: initialize with data SDK
async function init() {
await TeamData.ready; // or SoloData, SocialData, CmsData, VisiForm
const user = TeamData.getCurrentUser();
if (!user) {
TeamData.promptLogin();
return;
}
// Load and render data
const { records } = await TeamData.find('items');
console.log('Loaded', records.length, 'items');
}
init().catch(console.error);
</script>
</body>
</html>Common Patterns
Wait for SDK + Check Auth
async function init() {
await SocialData.ready; // MUST await before checking auth
const user = SocialData.getCurrentUser();
if (user) {
loadUserData();
} else {
showLoginPrompt();
}
}
init();Owner-Only Admin Panel
async function init() {
await CmsData.ready;
if (CmsData.isCreator()) {
document.getElementById('admin-panel').style.display = 'block';
}
loadPublicContent();
}Common Mistakes
Including SDK script tags — SoloData/TeamData/SocialData/CmsData/VisiForm are auto-injected. Never add
<script src="...">for them.Forgetting
data-page-id— Withoutdata-page-id="{{PAGE_ID}}"on the body tag, no SDK will work.Not awaiting
.ready— Alwaysawait SDK.readybefore calling any SDK method or checking auth.Reading HTML from public URL — Fetching from
/p/slugreturns HTML with injected runtime scripts. Always read fromGET /api/pages/:id.Using raw fetch() for data — Use the SDK methods (
TeamData.create(), etc.), not raw API calls. The SDKs handle auth, team scoping, and error handling.PageAssets vs data files — PageAssets are page-owned files (images, logos). For user-uploaded files within an app, use the file upload methods on TeamData/SoloData/SocialData.
Shallow merge on update —
SDK.update()does a shallow merge. To remove a field, explicitly set it tonull.
Design Guidelines
Use a clean, professional design with good contrast and readable fonts
Default to a light/white background unless the user requests otherwise
Use system fonts:
-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serifMake layouts responsive — test at mobile (375px), tablet (768px), and desktop (1200px+)
Use CSS Grid or Flexbox for layouts, not tables (unless showing tabular data)
Include loading states and empty states for data-driven UIs
For icons, use inline SVGs or Unicode symbols — no external icon libraries
For CDN libraries (Chart.js, etc.), use
https://cdn.jsdelivr.net/npm/
Error Codes
200 Success 201 Created 400 Bad request — check parameters 401 Unauthorized — invalid or expired token 403 Forbidden — insufficient permissions (not owner, wrong role) 404 Not found 429 Rate limited — wait and retry 500 Server error
Signaling Completion
When you are completely finished building or editing a page, call the agent-complete endpoint to signal that the page is ready. This publishes the page and updates the build status.
POST /api/pages/:id/agent-complete
Authorization: Bearer $TOKEN
# Response: { success: true, pageId: 123 }Full API Reference
For detailed endpoint parameters, response schemas, and advanced features, see the full documentation at /developers/api.
On this page
- Platform Overview
- Authentication
- Page Management
- HTML Rules
- Sending HTML via API
- Storage Modes
- Client-Side SDKs
- CMS Data
- Form Data
- Page Assets
- Page AI Features
- Starter Template
- Common Patterns
- Common Mistakes
- Design Guidelines
- Error Codes
- Signaling Completion