Simplio3D SDK v3.4
The official TypeScript SDK for the Simplio3D 3D CPQ platform. Full headless support for option blocks, conditional logic, pricing formulas, contact forms, 3D viewer, animations, and more.
Why Use the SDK?
The SDK provides a clean, type-safe wrapper around the Simplio3D REST API with built-in authentication, error handling, timeouts, and retries.
SDK Modules
Core
Projects, assets, materials, categories
Configurator
Option blocks, variants, conditions
CPQ & Forms
Pricing, formulas, quotes, forms
3D Viewer
Embed config, state, screenshots
Team & Sharing
Members, share links, email logs
Animations
Motion blocks, settings, branding
Installation
Get started with the Simplio3D SDK in your JavaScript or TypeScript project.
Using npm
npm install @simplio3d/sdkUsing yarn
yarn add @simplio3d/sdkUsing pnpm
pnpm add @simplio3d/sdkRequirements
- • Node.js 18.x or higher
- • Modern browser with ES2020+ support
- • TypeScript 5.0+ (optional, for type checking)
Quick Start
Get up and running with the Simplio3D SDK in minutes.
1. Import the SDK
import { createSimplio3DClient } from '@simplio3d/sdk';2. Initialize the Client
const client = createSimplio3DClient({
apiUrl: 'https://your-project.supabase.co/functions/v1/make-server-0532dd87',
accessToken: 'YOUR_ACCESS_TOKEN',
timeout: 30000,
retryAttempts: 3
});3. Build a Headless Configurator
// Load project and all configurator data
const project = await client.getProject('proj_123');
// SDK normalizes large-project response variants:
// - project.sceneData (when available)
// - project.sceneDataCompressed + project.sceneDataEncoding (fallback)
const blocks = await client.getOptionBlocks('proj_123');
const pricingBlocks = await client.getPricingBlocks('proj_123');
const formFields = await client.getFormFields('proj_123');
// Evaluate conditional logic based on user selections
const visibility = await client.evaluateConditions('proj_123', {
selections: {
dropdownSelections: { 'blk_frame': 'black' },
selectMaterialSelections: {},
checkboxSelections: {},
toggleSwitchSelections: { 'blk_armrests': 'with' },
carouselSelections: {}
}
});
// Calculate price (includes tax, formatting from project settings)
const price = await client.calculatePrice('proj_123', {
selections: { /* same as above */ },
variables: { width: 160 }
});
console.log('Total:', price.formatted); // "$1,223.99"
console.log('Subtotal:', price.formattedSubtotal); // "$1,019.99"
console.log('Tax:', price.formattedTax, price.taxLabel); // "$204.00 VAT"
// Submit quote
const quote = await client.submitQuote('proj_123', {
formData: { name: 'Jane', email: '[email protected]' },
configuration: { selections: { /* ... */ }, variables: { width: 160 } }
});Client Setup
Configure the Simplio3D client with various options.
Client Configuration
import { createSimplio3DClient } from '@simplio3d/sdk';
const client = createSimplio3DClient({
apiUrl: 'https://your-project.supabase.co/functions/v1/make-server-0532dd87',
accessToken: 'YOUR_ACCESS_TOKEN', // JWT from Supabase Auth
apiToken: 'YOUR_API_TOKEN', // Alternative: API token from Profile > API/SDK
timeout: 30000, // Request timeout (ms)
retryAttempts: 3, // Retry on network errors
onError: (error) => {
console.error('API Error:', error);
}
});Error Handling
import { AuthenticationError, NotFoundError, ValidationError } from '@simplio3d/sdk';
try {
const project = await client.getProject('project_123');
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Project not found');
} else if (error instanceof AuthenticationError) {
console.error('Session expired - redirect to login');
} else if (error instanceof ValidationError) {
console.error('Invalid data:', error.details);
} else {
console.error('Error:', error.message, 'Code:', error.code);
}
}Projects
Full CRUD for 3D projects. Types: configurator, viewer, cpq, ar-preview, embedded.
List Projects
const projects = await client.getProjects();
console.log(projects); // Project[]Get Project
const project = await client.getProject('proj_123');
console.log(project.name, project.type);
// Normalized scene access:
if (project.sceneData) {
console.log('Scene models:', project.sceneData.models?.length || 0);
} else if (project.sceneDataCompressed) {
// Runtime does not support decompression; consume compressed payload
console.log('Compressed scene payload:', project.sceneDataEncoding);
}Create Project
const newProject = await client.createProject({
name: 'Product Config',
type: 'configurator'
});
console.log(newProject.id);Update Project
const updated = await client.updateProject('proj_123', {
name: 'Updated Name',
data: { settings: { autoRotate: true } }
});Delete Project
await client.deleteProject('proj_123');Large-Project Handling in SDK
client.getProject() normalizes optimized API variants so consumers use a stable shape.
- • Lifts scene payloads from project.data when needed.
- • Tries to decompress gzip-base64 scene data when runtime supports DecompressionStream.
- • Ensures camera vectors and scene settings have safe defaults.
Option Blocks
Manage configurator option blocks: dropdowns, material selectors, checkboxes, toggles, carousels, text inputs, hotspots, pattern designers, design canvases, and more.
List Option Blocks
const blocks = await client.getOptionBlocks('proj_123');
blocks.forEach(b => {
console.log(b.name, b.type, b.dropdownVariants?.length);
});Create Dropdown Block
const block = await client.createOptionBlock('proj_123', {
type: 'dropdown',
name: 'Frame Color',
dropdownVariants: [
{
id: crypto.randomUUID(),
label: 'Matte Black',
value: 'matte-black',
targetPartNames: ['frame_body', 'frame_legs'],
materialData: {
baseColor: '#1a1a1a',
metallic: 0.8,
roughness: 0.4
}
},
{
id: crypto.randomUUID(),
label: 'Chrome Silver',
value: 'chrome-silver',
targetPartNames: ['frame_body', 'frame_legs'],
materialData: {
baseColor: '#C0C0C0',
metallic: 0.95,
roughness: 0.05
}
}
],
defaultDropdownValue: 'matte-black'
});Create Thumbnail Selector
const block = await client.createOptionBlock('proj_123', {
type: 'thumbnail-selector',
name: 'Fabric',
thumbnailStyle: 'grid',
thumbnailSize: '60x60',
thumbnailShape: 'rounded',
labelPosition: 'below',
dropdownVariants: [
{ id: crypto.randomUUID(), label: 'Linen', value: 'linen',
thumbnailUrl: 'https://cdn.example.com/linen.jpg',
materialData: { baseColor: '#f0e6d3', roughness: 0.9 } },
{ id: crypto.randomUUID(), label: 'Velvet', value: 'velvet',
thumbnailUrl: 'https://cdn.example.com/velvet.jpg',
materialData: { baseColor: '#2d1f4e', roughness: 0.95 } }
]
});Create Toggle with Visibility
const block = await client.createOptionBlock('proj_123', {
type: 'toggle-switch',
name: 'Headrest',
dropdownVariants: [
{ id: crypto.randomUUID(), label: 'Without', value: 'without',
visibilityConfig: { targetObjectIds: ['model_headrest'], action: 'hide' } },
{ id: crypto.randomUUID(), label: 'With Headrest', value: 'with',
visibilityConfig: { targetObjectIds: ['model_headrest'], action: 'show' } }
]
});Create Text Input Block
const block = await client.createOptionBlock('proj_123', {
type: 'text-input',
name: 'Custom Engraving',
textInputTargets: [{
id: crypto.randomUUID(),
label: 'Engraving Text',
targetPartName: 'nameplate',
placeholder: 'Enter your text',
maxLength: 30,
fontFamily: 'Montserrat',
fontSize: 36,
fontColor: '#gold',
textAlign: 'center',
canvasWidth: 1024,
canvasHeight: 256
}]
});Create Select Material — From Category (bulk)
// Auto-populates one swatch per material in the chosen category.
// Use this to expose large material libraries without authoring each variant by hand.
// Variants are synthesized at runtime as 'auto:{materialId}' — `dropdownVariants` stays empty.
// Pricing flows through the standard Pricing system (Price Group / Variable / Price Table /
// Unique Price) — link a pricing block to this option block to author per-material pricing.
const block = await client.createOptionBlock('proj_123', {
type: 'select-material',
name: 'Fabric',
materialSource: 'category', // ← opt into category mode
materialCategoryId: 'cat_woods', // ← owner-scoped category id
materialCategoryName: 'Woods',
// Target one or more 3D objects. Per-model parts: [] / omitted = every mesh in that model.
categoryTargetObjectIds: ['model_main_roof', 'model_extra_roofs'],
categoryTargetParts: {
model_main_roof: ['part_01', 'part_02'],
model_extra_roofs: [] // empty = all meshes in this object
},
categorySortOrder: 'name', // 'name' | 'newest' | 'oldest'
// Visual config (same as manual mode):
thumbnailStyle: 'grid',
thumbnailSize: '60x60',
thumbnailShape: 'rounded',
labelPosition: 'below'
});
// To switch back to manual authoring:
await client.updateOptionBlock('proj_123', block.id, {
materialSource: 'manual'
});Update Option Block
const updated = await client.updateOptionBlock('proj_123', 'blk_001', {
name: 'Updated Name',
visible: false,
dropdownVariants: [/* updated variants */]
});Reorder Blocks
const blocks = await client.reorderOptionBlocks('proj_123', {
blockIds: ['blk_003', 'blk_001', 'blk_002']
});
console.log('New order:', blocks.map(b => b.name));Conditional Logic
Dynamically show/hide blocks, variants, 3D objects, and individual mesh parts based on user selections. Attach rules to blocks, then evaluate at runtime.
Add Rules to a Block
// Hide "Armrest Color" when armrests toggle is off
await client.updateOptionBlock('proj_123', 'blk_armrest_color', {
conditionalRules: [{
id: crypto.randomUUID(),
action: 'hide',
targetScope: 'block',
targetVariantIds: [],
operator: 'all',
conditions: [{
id: crypto.randomUUID(),
sourceBlockId: 'blk_armrests',
matchType: 'is-selected',
sourceVariantIds: ['var_without']
}]
}]
});Variant-Level Rules
// Hide indoor-only fabrics when "Outdoor" is selected
await client.updateOptionBlock('proj_123', 'blk_fabric', {
conditionalRules: [{
id: crypto.randomUUID(),
action: 'hide',
targetScope: 'variants',
targetVariantIds: ['var_silk', 'var_velvet'],
operator: 'all',
conditions: [{
id: crypto.randomUUID(),
sourceBlockId: 'blk_environment',
matchType: 'is-selected',
sourceVariantIds: ['var_outdoor']
}]
}]
});3D Object Visibility Rules
// Hide 3D objects in the viewport without hiding the UI block
await client.updateOptionBlock('proj_123', 'blk_chair_parts', {
conditionalRules: [{
id: crypto.randomUUID(),
action: 'hide',
targetScope: '3d',
targetVariantIds: [],
target3dObjectNames: ['headrest_mesh', 'armrest_left', 'armrest_right'],
operator: 'all',
conditions: [{
id: crypto.randomUUID(),
sourceBlockId: 'blk_style',
matchType: 'is-selected',
sourceVariantIds: ['var_minimal']
}]
}]
});3D Part Visibility Rules
// Hide individual mesh parts (sub-meshes) in the viewport
// Targets specific part names from targetPartName / targetPartNames
// rather than whole objects (targetObjectName)
await client.updateOptionBlock('proj_123', 'blk_details', {
conditionalRules: [{
id: crypto.randomUUID(),
action: 'hide',
targetScope: '3d-parts',
targetVariantIds: [],
target3dPartNames: ['logo_emboss', 'stitching_detail', 'button_caps'],
operator: 'all',
conditions: [{
id: crypto.randomUUID(),
sourceBlockId: 'blk_finish',
matchType: 'is-selected',
sourceVariantIds: ['var_plain']
}]
}]
});Evaluate Conditions
const results = await client.evaluateConditions('proj_123', {
selections: {
dropdownSelections: { 'blk_frame': 'black' },
selectMaterialSelections: { 'blk_fabric': 'linen' },
checkboxSelections: { 'blk_acc': ['cup-holder'] },
toggleSwitchSelections: { 'blk_armrests': 'with' },
carouselSelections: {}
}
});
results.forEach(r => {
console.log(r.blockId, 'visible:', r.blockVisible);
if (r.hiddenVariantIds.length > 0) {
console.log(' hidden variants:', r.hiddenVariantIds);
}
if (r.hidden3dObjectNames?.length > 0) {
console.log(' hidden 3D objects:', r.hidden3dObjectNames);
}
if (r.hidden3dPartNames?.length > 0) {
console.log(' hidden 3D parts:', r.hidden3dPartNames);
}
});Target Scopes Reference
| Scope | Effect | Target Field |
|---|---|---|
| block | Hides/shows the entire option block UI | targetVariantIds (ignored) |
| variants | Hides/shows specific variants within the block | targetVariantIds |
| 3d | Hides/shows whole 3D objects in the viewport (UI stays visible) | target3dObjectNames |
| 3d-parts | Hides/shows individual mesh parts in the viewport (UI stays visible) | target3dPartNames |
Pricing & CPQ
Configure and calculate prices with base prices, lookup tables, variables, and formulas.
List Pricing Blocks
const blocks = await client.getPricingBlocks('proj_123');
blocks.forEach(b => {
console.log(b.name, b.type, b.basePrice || b.variableKey);
});Create Base Price
const block = await client.createPricingBlock('proj_123', {
type: 'base-price',
name: 'Base Price',
basePrice: 499.99,
baseCurrency: 'USD'
});Create Variable
const widthVar = await client.createPricingBlock('proj_123', {
type: 'variable',
name: 'Width',
variableKey: 'width',
variableDefaultValue: 120,
variableMin: 80,
variableMax: 200,
variableStep: 10,
variableUnit: 'cm'
});Update Price Table
await client.updatePricingBlock('proj_123', 'pb_3', {
tableRows: [
{ id: 'r1', cells: { col_1: 'oak' }, priceAdjustment: 0 },
{ id: 'r2', cells: { col_1: 'walnut' }, priceAdjustment: 120 },
{ id: 'r3', cells: { col_1: 'marble' }, priceAdjustment: 399 }
]
});Calculate Price
const price = await client.calculatePrice('proj_123', {
selections: {
dropdownSelections: { 'blk_wood': 'walnut' },
selectMaterialSelections: {},
checkboxSelections: { 'blk_accessories': ['cup-holder'] },
toggleSwitchSelections: { 'blk_armrests': 'with' },
carouselSelections: {}
},
variables: { width: 160, quantity: 2 }
});
console.log('Base:', price.basePrice);
console.log('Adjustments:', price.adjustments);
console.log('Total:', price.totalPrice, price.currency);
console.log('Formatted:', price.formatted); // "$1,223.99"
console.log('Tax:', price.formattedTax); // "$204.00"
console.log('Subtotal:', price.formattedSubtotal); // "$1,019.99"
// Tax, formatting, and currency are applied based on Project Settings > PriceContact Forms
Build and manage contact/quote request forms with configurable fields.
List Form Fields
const fields = await client.getFormFields('proj_123');
fields.forEach(f => {
console.log(f.type, f.label, f.required ? '(required)' : '');
});Create Form Field
const field = await client.createFormField('proj_123', {
type: 'dropdown',
label: 'Budget Range',
required: true,
width: 'half',
options: [
{ id: '1', label: 'Under $1k', value: 'under-1k' },
{ id: '2', label: '$1k - $5k', value: '1k-5k' },
{ id: '3', label: '$5k - $10k', value: '5k-10k' },
{ id: '4', label: 'Over $10k', value: 'over-10k' }
],
allowOther: true
});Quote Submissions
Submit and manage quote requests with form data, configuration selections, and pricing.
Submit a Quote
const submission = await client.submitQuote('proj_123', {
formData: {
name: 'Jane Smith',
email: '[email protected]',
phone: '+1 555 0123',
message: 'Need a quote for 50 units.'
},
configuration: {
selections: {
dropdownSelections: { 'blk_frame': 'black', 'blk_wood': 'walnut' },
selectMaterialSelections: { 'blk_fabric': 'linen' },
checkboxSelections: { 'blk_accessories': ['cup-holder'] },
toggleSwitchSelections: { 'blk_armrests': 'with' },
carouselSelections: {}
},
variables: { width: 160, quantity: 50 },
screenshotUrl: 'https://cdn.example.com/screenshot.png'
}
});
console.log('Submitted:', submission.id, submission.status);List Submissions
const submissions = await client.getQuoteSubmissions('proj_123');
submissions.forEach(s => {
console.log(s.customerName, s.customerEmail, s.status);
console.log('Config:', s.configuration.pricing?.totalPrice);
});3D Viewer
Embed and control the 3D viewer. Manage camera, lighting, state, and take screenshots.
Get Embed Config
const config = await client.getViewerEmbedConfig('proj_123');
console.log('Camera:', config.cameraPosition);
console.log('Environment:', config.environmentPreset);
console.log('AR enabled:', config.enableAR);Get Viewer State
const state = await client.getViewerState('proj_123');
console.log('Loaded:', state.loaded);
console.log('Selections:', state.selections);
console.log('Visible blocks:', state.visibleBlocks);
console.log('Price:', state.currentPrice?.totalPrice);Set Viewer State
const newState = await client.setViewerState('proj_123', {
loaded: true,
selections: {
dropdownSelections: { 'blk_frame': 'silver' },
selectMaterialSelections: {},
checkboxSelections: {},
toggleSwitchSelections: {},
carouselSelections: {}
},
visibleBlocks: ['blk_001', 'blk_002'],
cameraPosition: [3, 2, 5],
cameraTarget: [0, 0.5, 0]
});Animations
Define looping animations: move, rotation, float, scale-pulse, swing, orbit.
List Animations
const anims = await client.getAnimationBlocks('proj_123');
anims.forEach(a => console.log(a.name, a.type, a.speed));Create Animation
const anim = await client.createAnimationBlock('proj_123', {
type: 'float',
name: 'Hover Effect',
targetPartNames: ['product_body'],
axis: 'y',
speed: 0.4,
amplitude: 0.3,
easing: 'ease-in-out',
loopMode: 'ping-pong'
});
console.log(anim.id);Project Settings
Configure branding, lighting, camera, loading screens, email, e-commerce, and webhooks.
Update Settings
const project = await client.updateProjectSettings('proj_123', {
// Branding
viewportBackgroundColor: '#1a1a2e',
primaryAccentColor: '#e94560',
actionButtonBgColor: '#e94560',
actionButtonHoverBgColor: '#c73450',
actionButtonTextColor: '#ffffff',
fontFamily: 'poppins',
sidebarWidth: 400,
compactMode: true,
// Lighting
environmentPreset: 'sunset',
lightIntensity: 1.2,
enableShadows: true,
// Camera
cameraFOV: 45,
enableAutoRotate: true,
autoRotateSpeed: 0.5,
// Features
enableAR: true,
showPrice: true,
enableForm: true,
// Webhooks
webhookUrl: 'https://your-server.com/webhook',
webhookEvents: ['configuration_changed', 'quote_requested'],
// Analytics
googleAnalyticsId: 'G-XXXXXXXXXX'
});Assets
Upload and manage 3D models, textures, and graphics.
List Assets
const assets = await client.getAssets('3d');
assets.forEach(a => console.log(a.name, a.format, a.fileSize));Create Asset
const asset = await client.createAsset({
name: 'Chair Model',
type: '3d',
url: 'https://storage.example.com/chair.glb',
fileSize: 1024000,
category: 'furniture'
});Get Download URL
const url = await client.getAssetDownloadUrl('asset_123');
// Returns a time-limited signed URLDelete Asset
await client.deleteAsset('asset_123');Materials
PBR materials with base color, metallic, roughness, normal maps.
List Materials
const materials = await client.getMaterials();
materials.forEach(m => console.log(m.name, m.pbr.baseColor));Create Material
const material = await client.createMaterial({
name: 'Brushed Metal',
category: 'metals',
pbr: { baseColor: '#808080', metallic: 0.9, roughness: 0.3 }
});Update Material
await client.updateMaterial('mat_123', {
pbr: { roughness: 0.5, metallic: 0.8 }
});Delete Material
await client.deleteMaterial('mat_123');Categories
Organize assets and materials into categories.
List Categories
const categories = await client.getCategories('3d');Create Category
const category = await client.createCategory({
name: 'Furniture',
type: '3d'
});Update Category
await client.updateCategory('cat_123', { name: 'Updated' });Delete Category
await client.deleteCategory('cat_123');Team Management
Manage team members with role-based access (admin, editor, viewer).
List Members
const members = await client.getTeamMembers();
members.forEach(m => console.log(m.name, m.role, m.status));Invite Member
const member = await client.inviteTeamMember({
email: '[email protected]',
role: 'editor',
name: 'Jane Smith'
});Update Member
await client.updateTeamMember('mem_123', { role: 'admin' });Remove Member
await client.removeTeamMember('mem_123');Project Sharing
Generate public share links with password protection and expiry.
Get Share Config
const share = await client.getShareConfig('proj_123');
console.log(share.shareUrl, share.viewCount);Enable Sharing
const share = await client.enableSharing('proj_123');
console.log(share.shareUrl);Update Share Settings
await client.updateShareConfig('proj_123', {
allowDownload: true,
passwordProtected: true,
password: 'secret123',
expiresAt: '2026-06-01T00:00:00Z'
});Regenerate Token
const newShare = await client.regenerateShareToken('proj_123');
console.log(newShare.shareUrl);Email Logs
View delivery logs, delete entries, and resend failed emails.
Get Logs
const logs = await client.getEmailLogs();
logs.forEach(log => console.log(log.type, log.recipient, log.status));Delete Log
await client.deleteEmailLog('log_123');Resend Failed Email
await client.resendEmail('log_123');Webhooks
Configure HTTP callbacks to receive real-time event notifications. Supports CRUD, test pings, delivery logs, and HMAC-SHA256 signature verification.
List Webhooks
const webhooks = await client.getWebhooks('proj_123');
webhooks.forEach(wh => console.log(wh.name, wh.url, wh.enabled, wh.events));Create Webhook
const webhook = await client.createWebhook('proj_123', {
name: 'Order Alerts',
url: 'https://your-server.com/webhook',
events: ['quote.submitted', 'form.submitted', 'price.calculated'],
secret: 'whsec_my_signing_secret',
enabled: true
});
console.log(webhook.id, webhook.createdAt);Update Webhook
const updated = await client.updateWebhook('proj_123', 'wh_abc', {
events: ['quote.submitted', 'quote.updated', 'form.submitted'],
enabled: true
});
console.log('Events:', updated.events);Delete Webhook
await client.deleteWebhook('proj_123', 'wh_abc');Test Webhook
const result = await client.testWebhook('proj_123', 'wh_abc');
console.log(result.statusCode, result.delivered);
// { statusCode: 200, statusText: "OK", delivered: true }Get Delivery Logs
const deliveries = await client.getWebhookDeliveries('proj_123', 'wh_abc');
deliveries.forEach(d => {
console.log(d.event, d.delivered, d.statusCode, d.timestamp);
});Trigger Webhooks
const result = await client.triggerWebhooks('proj_123', {
event: 'quote.submitted',
data: { quoteId: 'q_456', total: 1299.99 }
});
console.log(`Triggered ${result.triggered} webhooks`);Verify Signature (Node.js)
// Verify incoming webhook signatures
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(JSON.stringify(body))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature), Buffer.from(expected)
);
}
// In your Express handler:
app.post('/webhook', (req, res) => {
const sig = req.headers['x-simplio3d-signature'];
if (!verifyWebhook(req.body, sig, 'whsec_...')) {
return res.status(401).send('Invalid signature');
}
console.log('Event:', req.headers['x-simplio3d-event']);
res.sendStatus(200);
});Augmented Reality (AR) SDK
SDK helpers for AR file management, analytics tracking, and programmatic AR launches.
Upload USDZ File
// Upload a USDZ file for iOS AR Quick Look
const usdzFile = document.querySelector('input[type="file"]').files[0];
const result = await client.projects.uploadUsdz('PROJECT_ID', usdzFile);
console.log(result.usdzId, result.usdzPath);
// Then update project settings:
await client.projects.updateSettings('PROJECT_ID', {
enableAR: true,
arUsdzFileName: result.fileName,
arUsdzModelPath: result.usdzPath,
arUsdzModelId: result.usdzId,
arAutoLaunch: true,
arAnalyticsEnabled: true,
});Track AR Events
import { trackAREvent } from './lib/ar-analytics';
// Track an AR event (fire-and-forget, non-blocking)
trackAREvent({
event: 'ar_launch_ios',
projectId: 'PROJECT_ID',
meta: { source: 'floating_tools' },
});
// Available event types:
// 'ar_dialog_open' — Desktop QR dialog opened
// 'ar_qr_copy' — AR link copied
// 'ar_qr_open_tab' — AR link opened in new tab
// 'ar_launch_ios' — iOS AR Quick Look launched
// 'ar_launch_android' — Android Scene Viewer launched
// 'ar_auto_launch' — Auto-launched via ?ar=1
// 'ar_page_view' — Share page loaded with ARFetch AR Analytics
import { fetchARAnalytics } from './lib/ar-analytics';
const analytics = await fetchARAnalytics('PROJECT_ID', accessToken);
console.log(analytics.totalEvents); // 142
console.log(analytics.byEvent); // { ar_launch_ios: 41, ... }
console.log(analytics.recentEvents); // last 50 eventsProgrammatic AR Launch
// Generate an AR share URL
const shareUrl = `https://yourapp.com/share/${projectId}/${token}`;
const arUrl = `${shareUrl}?ar=1`;
// On iOS — use USDZ with AR Quick Look
const link = document.createElement('a');
link.setAttribute('rel', 'ar');
link.href = usdzSignedUrl; // from /share/:projectId/usdz
const img = document.createElement('img');
img.style.display = 'none';
link.appendChild(img);
document.body.appendChild(link);
link.click();
// On Android — use Scene Viewer intent
const intent = `intent://arvr.google.com/scene-viewer/1.0
?file=${encodeURIComponent(glbUrl)}
&mode=ar_preferred#Intent;...;end;`;
window.location.href = intent;TypeScript Support
Full type definitions for all models, requests, and responses.
Type Imports
import type {
// Client & config
Simplio3DConfig,
// Core models
Project, Material, Asset, Category,
ProjectType, AssetType, CategoryType,
// Request types
CreateProjectRequest, UpdateProjectRequest,
CreateMaterialRequest, UpdateMaterialRequest,
CreateAssetRequest, CreateCategoryRequest,
// Option blocks (Headless Configurator)
OptionBlock, OptionBlockType, DropdownVariant,
CreateOptionBlockRequest, UpdateOptionBlockRequest,
ReorderBlocksRequest,
// Conditional logic
ConditionalRule, ConditionalCondition,
EvaluateConditionsRequest, BlockVisibilityResult,
// Pricing & CPQ
PricingBlock, PricingBlockType, PriceTableRow, PriceTableColumn,
FormulaToken, CalculatePriceRequest, CalculatePriceResponse,
// Contact forms
FormField, FormFieldType, FormFieldOption,
CreateFormFieldRequest,
// Quote submissions
QuoteSubmission, SubmitQuoteRequest,
// 3D Viewer
ViewerEmbedConfig, ViewerState, SceneManipulation,
// Animations
AnimationBlock, AnimationBlockType, AnimationAxis,
AnimationEasing, AnimationLoopMode,
// Project settings
ProjectSettingsUpdate,
// Team & sharing
TeamMember, InviteMemberRequest, UpdateMemberRequest,
MemberRole, MemberStatus,
ShareConfig, UpdateShareRequest,
// Email & auth
EmailLogEntry, UserProfile, ApiTokenResponse,
// Errors
Simplio3DError, AuthenticationError,
ValidationError, NotFoundError,
} from '@simplio3d/sdk';Examples
Real-world examples covering common headless configurator workflows.
Complete Headless Configurator
import { createSimplio3DClient } from '@simplio3d/sdk';
async function buildConfigurator(projectId: string, accessToken: string) {
const client = createSimplio3DClient({
apiUrl: process.env.SIMPLIO3D_API_URL,
accessToken
});
// 1. Load all configurator data
const [project, blocks, pricing, formFields] = await Promise.all([
client.getProject(projectId),
client.getOptionBlocks(projectId),
client.getPricingBlocks(projectId),
client.getFormFields(projectId)
]);
// 2. Build selection state from defaults
const selections = {
dropdownSelections: {} as Record<string, string>,
selectMaterialSelections: {} as Record<string, string>,
checkboxSelections: {} as Record<string, string[]>,
toggleSwitchSelections: {} as Record<string, string>,
carouselSelections: {} as Record<string, string>,
};
blocks.forEach(block => {
if (block.defaultDropdownValue && block.type === 'dropdown') {
selections.dropdownSelections[block.id] = block.defaultDropdownValue;
}
if (block.defaultCheckboxValues && block.type === 'checkbox') {
selections.checkboxSelections[block.id] = block.defaultCheckboxValues;
}
});
// 3. Evaluate conditional visibility
const visibility = await client.evaluateConditions(projectId, { selections });
// 4. Calculate initial price
const price = await client.calculatePrice(projectId, {
selections,
variables: {}
});
return {
project,
blocks: blocks.filter(b => {
const vis = visibility.find(v => v.blockId === b.id);
return vis ? vis.blockVisible : true;
}),
pricing,
formFields,
price,
selections,
visibility
};
}React Hook: useConfigurator
import { createSimplio3DClient } from '@simplio3d/sdk';
import { useState, useEffect, useCallback, useMemo } from 'react';
function useConfigurator(projectId: string, accessToken: string) {
const client = useMemo(() => createSimplio3DClient({
apiUrl: import.meta.env.VITE_API_URL,
accessToken
}), [accessToken]);
const [blocks, setBlocks] = useState([]);
const [selections, setSelections] = useState({
dropdownSelections: {},
selectMaterialSelections: {},
checkboxSelections: {},
toggleSwitchSelections: {},
carouselSelections: {},
});
const [visibility, setVisibility] = useState([]);
const [price, setPrice] = useState(null);
const [loading, setLoading] = useState(true);
// Load initial data
useEffect(() => {
Promise.all([
client.getOptionBlocks(projectId),
client.getPricingBlocks(projectId)
]).then(([blocks]) => {
setBlocks(blocks);
setLoading(false);
});
}, [client, projectId]);
// Re-evaluate when selections change
useEffect(() => {
client.evaluateConditions(projectId, { selections })
.then(setVisibility);
client.calculatePrice(projectId, { selections, variables: {} })
.then(setPrice);
}, [client, projectId, selections]);
const setSelection = useCallback((blockId, value) => {
setSelections(prev => ({
...prev,
dropdownSelections: { ...prev.dropdownSelections, [blockId]: value }
}));
}, []);
return { blocks, selections, setSelection, visibility, price, loading };
}E-commerce Integration
// Add configured product to cart with pricing
async function addToCart(projectId, selections, variables) {
const client = createSimplio3DClient({ apiUrl: API_URL, accessToken });
// Get final price
const price = await client.calculatePrice(projectId, {
selections,
variables
});
// Get a screenshot of the current configuration
const screenshotUrl = 'https://cdn.example.com/screenshot.png';
// Submit as a quote (or redirect to cart)
const submission = await client.submitQuote(projectId, {
formData: { name: user.name, email: user.email },
configuration: {
selections,
variables,
screenshotUrl
}
});
// Or send to your e-commerce platform
await fetch('/api/cart/add', {
method: 'POST',
body: JSON.stringify({
productId: projectId,
price: price.totalPrice,
currency: price.currency,
configurationId: submission.id,
thumbnail: screenshotUrl,
options: selections
})
});
}SDK Changelog
Track all SDK changes, new methods, deprecations, and improvements across versions.
SDK Changelog
Track all changes, updates, and improvements
v3.4
patchLatest✨ New Features
- •Large Project Transport + Async Submit Semantics: getProject() now normalizes optimized large-project API variants into a stable SDK shape.
- •SDK now attempts gzip-base64 scene decompression when runtime supports DecompressionStream and exposes fallback compressed fields otherwise.
⚡ Improvements
- •Camera vectors and scene settings from mixed transport formats are normalized with safe defaults for consistent consumer behavior.
