Skip to content

Endpoints

Full reference for every Vivreal API endpoint

intermediate15 min readFor developers

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:

ParamTypeRequiredDescription
siteIdstringYesYour 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 — with currentFile.source containing a signed CDN URL
  • heroImage — same format as logo
  • businessInfo — name, address, contact info, shipping settings
  • pages — page configurations (routes, collection bindings)

Try it

GET/tenant/siteDetails
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ✎ 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();

Edit the code and switch to Preview to see the result

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:

ParamTypeRequiredDescription
collectionIdstringYesThe collection to fetch from
limitnumberNoMax items to return (up to 100)
skipnumberNoPagination offset (default 0)
sortstringNoSort field and direction, e.g. publishDate:desc or createdAt:desc,name:asc
filters[key]stringNoFilter 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

GET/tenant/collectionObjects
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ✎ 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();

Edit the code and switch to Preview to see the result

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:

ParamTypeRequiredDescription
typestringYesIntegration type (e.g., stripe)
accountHandlestringNoAccount identifier for multi-account integrations (max 200 chars)
groupBy[key]stringNoGroup results by this field name
groupBy[value]stringNoGroup results matching this field value
filters[key]stringNoFilter by field (e.g., filters[productType]=Bread)
searchstringNoSearch across names and descriptions (max 200 chars)
limitnumberNoMax items to return (up to 100)
skipnumberNoPagination offset (default 0)
sortstringNoSort 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

GET/tenant/integrationObjects
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ✎ 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();

Edit the code and switch to Preview to see the result

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:

ParamTypeRequiredDescription
tokenstringYesPreview 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:

FieldTypeRequiredDescription
productsarrayYesArray of { price: "stripe_price_id", quantity: number }
requiresShippingbooleanYesWhether to collect a shipping address
originUrlstringNoURL to redirect back to after checkout (defaults to request origin)
businessNamestringNoBusiness name for the receipt
contactEmailstringNoCustomer 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

POST/tenant/createCheckoutSession
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ✎ 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();

Edit the code and switch to Preview to see the result

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:

FieldTypeRequiredDescription
collectionIdstringYesCollection ID to store the object in
typestringYesObject type: createReview or subscribeUser
objectValueobjectYesThe 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:

FieldTypeRequiredDescription
namestringYesSender's name
tostringYesRecipient email address
messagestringYesThe message
siteNamestringYesYour site name (used in email subject)
customHtmlstringNoCustom HTML email body (overrides default template)
htmlInfoobjectNoTemplate 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:

FieldTypeRequiredDescription
tostringYesCustomer email address
htmlInfoobjectYesEmail template data
htmlInfo.titlestringYesEmail heading (e.g., "Order Confirmed!")
htmlInfo.subtitlestringYesSubheading text
htmlInfo.whiteBoxTextstringYesMain content block (order details)
htmlInfo.signaturestringYesClosing 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:

RuleResult
publishDate in the futureHidden
publishDate in the past or unsetVisible
archived is trueHidden
archived is missing or falseVisible

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 first
  • objectValue.name:asc — alphabetical
  • createdAt:desc — recently created
  • publishDate:desc,name:asc — newest first, then alphabetical