VISIMADE
← Developer Hub

Getting Started

  • Agent Guide
  • Authentication
  • AI Coding Agents

Core APIs

Data APIs

Guides

Reference

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 time

  • Single 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-injected

  • Make 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 output

Storage 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.SoloData

  • team_app — Shared team data with roles (owner/admin/member/viewer). SDK: window.TeamData

  • social_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 modal
TeamData (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 permission
SocialData (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 modal

Form 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 in

Page 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/:assetId
Client-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
  1. Including SDK script tags — SoloData/TeamData/SocialData/CmsData/VisiForm are auto-injected. Never add <script src="..."> for them.

  2. Forgetting data-page-id — Without data-page-id="{{PAGE_ID}}" on the body tag, no SDK will work.

  3. Not awaiting .ready — Always await SDK.ready before calling any SDK method or checking auth.

  4. Reading HTML from public URL — Fetching from /p/slug returns HTML with injected runtime scripts. Always read from GET /api/pages/:id.

  5. 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.

  6. 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.

  7. Shallow merge on updateSDK.update() does a shallow merge. To remove a field, explicitly set it to null.


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-serif

  • Make 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