Endpoints
Base URL: https://client.vivreal.io
All endpoints are under /tenant/. Every request requires your API key in the Authorization header.
GET /tenant/siteDetails
Fetch your site's configuration — theme colors, logo, hero image, business info, and page configs.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
siteId | string | Yes | Your site's ID |
curl "https://client.vivreal.io/tenant/siteDetails?siteId=YOUR_SITE_ID" \
-H "Authorization: YOUR_API_KEY"
Response includes:
- Theme colors (
primary,secondary,surface,text-primary, etc.) logo— withcurrentFile.sourcecontaining a signed CDN URLheroImage— same format as logobusinessInfo— name, address, contact info, shipping settingspages— page configurations (routes, collection bindings)
Try it
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ✎ UPDATE THESE WITH YOUR REAL VALUES // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var API_KEY = 'your-api-key-here'; var SITE_ID = 'your-site-id-here'; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var BASE = 'https://client.vivreal.io'; async function run() { if (isPlaceholder(API_KEY) || isPlaceholder(SITE_ID)) { showMock(MOCK, ['API_KEY', 'SITE_ID']); return; } try { var res = await fetch( BASE + '/tenant/siteDetails?siteId=' + SITE_ID, { headers: { Authorization: API_KEY } } ); var json = await res.json(); if (!json.success) throw new Error(json.error); showResult(json); } catch (err) { showError(err.message); } } run();
GET /tenant/collectionObjects
Fetch published items from a collection. Blog posts, events, team members, FAQ — whatever you've set up in the portal.
Only items with publishDate in the past and not archived are returned.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
collectionId | string | Yes | The collection to fetch from |
limit | number | No | Max items to return (up to 100) |
skip | number | No | Pagination offset (default 0) |
sort | string | No | Sort field and direction, e.g. publishDate:desc or createdAt:desc,name:asc |
filters[key] | string | No | Filter by field value, e.g. filters[category]=News |
curl "https://client.vivreal.io/tenant/collectionObjects?collectionId=abc123&limit=10&sort=publishDate:desc" \
-H "Authorization: YOUR_API_KEY"
Response:
{
"success": true,
"data": {
"items": [
{
"_id": "64a1b2c3d4e5f6...",
"objectValue": {
"title": "My Blog Post",
"body": "<p>Content here...</p>",
"image": {
"name": "hero.jpg",
"key": "groupObjects/.../hero.jpg",
"type": "image",
"currentFile": {
"source": "https://media.vivreal.io/...?Signature=..."
}
},
"mediaFields": { "hero.jpg": "image" }
},
"collectionObj": { "name": "Blog", "refID": "abc123" },
"publishDate": "2026-03-01T00:00:00.000Z",
"archived": false
}
],
"totalCount": 42
}
}
Images in Collection Objects
Objects with images have a mediaFields map that tells you which fields contain media. The API resolves these to signed CloudFront CDN URLs automatically.
// Get the signed image URL
const imageUrl = item.objectValue.image.currentFile.source;
Signed URLs expire
CDN URLs have a limited TTL. Don't cache them long-term — fetch fresh data when serving content.
Try it
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ✎ UPDATE THESE WITH YOUR REAL VALUES // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var API_KEY = 'your-api-key-here'; var COLLECTION_ID = 'your-collection-id'; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var BASE = 'https://client.vivreal.io'; async function run() { if (isPlaceholder(API_KEY) || isPlaceholder(COLLECTION_ID)) { showMock(MOCK, ['API_KEY', 'COLLECTION_ID']); return; } try { var params = new URLSearchParams({ collectionId: COLLECTION_ID, limit: '10', sort: 'publishDate:desc', }); var res = await fetch( BASE + '/tenant/collectionObjects?' + params, { headers: { Authorization: API_KEY } } ); var json = await res.json(); if (!json.success) throw new Error(json.error); showResult(json); } catch (err) { showError(err.message); } } run();
GET /tenant/integrationObjects
Fetch products from your connected integrations (e.g., Stripe products).
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Integration type (e.g., stripe) |
accountHandle | string | No | Account identifier for multi-account integrations (max 200 chars) |
groupBy[key] | string | No | Group results by this field name |
groupBy[value] | string | No | Group results matching this field value |
filters[key] | string | No | Filter by field (e.g., filters[productType]=Bread) |
search | string | No | Search across names and descriptions (max 200 chars) |
limit | number | No | Max items to return (up to 100) |
skip | number | No | Pagination offset (default 0) |
sort | string | No | Sort field and direction, e.g. objectValue.name:asc |
curl "https://client.vivreal.io/tenant/integrationObjects?type=stripe&sort=objectValue.name:asc" \
-H "Authorization: YOUR_API_KEY"
Product fields include name, price, description, productImage, default_price (Stripe price ID), and productType.
Variant Products
Some products have variants (e.g., sizes, colors). Variant fields are objects keyed by variant name instead of simple strings:
{
"name": { "Small": "Small Sourdough", "Large": "Large Sourdough" },
"price": { "Small": "6.99", "Large": "10.99" },
"default_price": { "Small": "price_sm123", "Large": "price_lg456" }
}
Check typeof field === "string" to determine if you're dealing with a simple or variant product.
Try it
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ✎ UPDATE THIS WITH YOUR REAL VALUE // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var API_KEY = 'your-api-key-here'; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var BASE = 'https://client.vivreal.io'; async function run() { if (isPlaceholder(API_KEY)) { showMock(MOCK, ['API_KEY']); return; } try { var params = new URLSearchParams({ type: 'stripe', sort: 'objectValue.name:asc', }); // Optional: filter by product type // params.set('filters[productType]', 'Bread'); // Optional: search // params.set('search', 'sourdough'); var res = await fetch( BASE + '/tenant/integrationObjects?' + params, { headers: { Authorization: API_KEY } } ); var json = await res.json(); if (!json.success) throw new Error(json.error); showResult(json); } catch (err) { showError(err.message); } } run();
GET /tenant/preview
Fetch draft or unpublished content using a preview token. Unlike other endpoints, this bypasses the publishDate and archived filters.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Preview token generated from the portal |
curl "https://client.vivreal.io/tenant/preview?token=PREVIEW_TOKEN" \
-H "Authorization: YOUR_API_KEY"
Preview tokens
Preview tokens are generated in the Vivreal portal when you share a preview link for draft content. They grant temporary access to a single unpublished object.
POST /tenant/createCheckoutSession
Create a Stripe checkout session. The Stripe secret key is resolved automatically from your group's integration — you never handle it directly.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
products | array | Yes | Array of { price: "stripe_price_id", quantity: number } |
requiresShipping | boolean | Yes | Whether to collect a shipping address |
originUrl | string | No | URL to redirect back to after checkout (defaults to request origin) |
businessName | string | No | Business name for the receipt |
contactEmail | string | No | Customer email for the receipt |
curl -X POST "https://client.vivreal.io/tenant/createCheckoutSession" \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"products": [{ "price": "price_abc123", "quantity": 2 }],
"requiresShipping": true,
"originUrl": "https://mysite.com"
}'
Response: Returns a Stripe checkout session URL string. Redirect your customer to this URL to complete the purchase.
Try it
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ✎ UPDATE THESE WITH YOUR REAL VALUES // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var API_KEY = 'your-api-key-here'; var PRICE_IDS = ['price_abc123', 'price_def456']; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var BASE = 'https://client.vivreal.io'; async function run() { if (isPlaceholder(API_KEY)) { setOutput( '<span class="badge">SAMPLE DATA</span>' + '<p style="color:#94a3b8;font-size:13px;margin:0 0 12px">' + 'Replace <code style="color:#818cf8">API_KEY</code> and ' + '<code style="color:#818cf8">PRICE_IDS</code> above to create a live session.</p>' + '<p style="color:#64748b;font-size:12px;margin:0 0 6px">Request body:</p>' + '<pre>' + renderJson(MOCK_REQUEST, 0) + '</pre>' + '<p style="color:#64748b;font-size:12px;margin:12px 0 6px">Response:</p>' + '<pre>' + renderJson(MOCK_RESPONSE, 0) + '</pre>' + '<p style="color:#94a3b8;font-size:13px;margin:12px 0 0">' + 'Redirect the customer to the returned URL.</p>' ); return; } try { var res = await fetch(BASE + '/tenant/createCheckoutSession', { method: 'POST', headers: { Authorization: API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ products: PRICE_IDS.map(function(id) { return { price: id, quantity: 1 }; }), requiresShipping: true, originUrl: window.location.origin, }), }); var json = await res.json(); if (!res.ok) throw new Error(json.error || 'Checkout failed'); setOutput( '<p style="color:#86efac;margin:0 0 12px">Session created!</p>' + '<pre>' + renderJson(json, 0) + '</pre>' ); } catch (err) { showError(err.message); } } run();
POST /tenant/definedCollectionObject
Create user-submitted content like product reviews or newsletter signups. Only specific object types are allowed.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
collectionId | string | Yes | Collection ID to store the object in |
type | string | Yes | Object type: createReview or subscribeUser |
objectValue | object | Yes | The object data (fields depend on your collection schema) |
curl -X POST "https://client.vivreal.io/tenant/definedCollectionObject" \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"collectionId": "col_abc123",
"type": "createReview",
"objectValue": {
"rating": 5,
"title": "Great product!",
"body": "Really impressed with the quality.",
"author": "Jane Doe"
}
}'
Allowed types
Only createReview and subscribeUser types are accepted. This endpoint is designed for user-generated content from your website — not general-purpose object creation.
POST /tenant/sendContactEmail
Send a contact form submission to the site owner.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Sender's name |
to | string | Yes | Recipient email address |
message | string | Yes | The message |
siteName | string | Yes | Your site name (used in email subject) |
customHtml | string | No | Custom HTML email body (overrides default template) |
htmlInfo | object | No | Template data: { title, subtitle, whiteBoxText, signature } |
Template required
You must provide either customHtml or htmlInfo — at least one is required to generate the email.
curl -X POST "https://client.vivreal.io/tenant/sendContactEmail" \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"to": "owner@mysite.com",
"message": "I have a question about your products.",
"siteName": "My Store"
}'
Response (201): Returns true on success.
POST /tenant/sendOrderPlacedEmail
Send an order confirmation email to the customer after a successful checkout.
Copy endpoint context for AI
Copy this context and paste it into your AI assistant for relevant help.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Customer email address |
htmlInfo | object | Yes | Email template data |
htmlInfo.title | string | Yes | Email heading (e.g., "Order Confirmed!") |
htmlInfo.subtitle | string | Yes | Subheading text |
htmlInfo.whiteBoxText | string | Yes | Main content block (order details) |
htmlInfo.signature | string | Yes | Closing signature |
curl -X POST "https://client.vivreal.io/tenant/sendOrderPlacedEmail" \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "customer@example.com",
"htmlInfo": {
"title": "Order Confirmed!",
"subtitle": "Thank you for your purchase",
"whiteBoxText": "Order #1234 — 2x Sourdough Loaf ($17.98)",
"signature": "The My Store Team"
}
}'
Response (201): Returns true on success.
Post-checkout flow
Typically called after a successful createCheckoutSession redirect. Use this to send the customer their order confirmation with the details from the completed checkout.
Content Filtering
The API automatically enforces these visibility rules on collections and products:
| Rule | Result |
|---|---|
publishDate in the future | Hidden |
publishDate in the past or unset | Visible |
archived is true | Hidden |
archived is missing or false | Visible |
You don't need to filter these yourself — the API handles it.
Sorting
Sort format is field:direction where direction is asc or desc. Supports multiple fields separated by commas.
Common sort fields:
publishDate:desc— newest firstobjectValue.name:asc— alphabeticalcreatedAt:desc— recently createdpublishDate:desc,name:asc— newest first, then alphabetical