@cymmetrik-dev/3d-transfer-sdk
v1.6.1
Published
TypeScript SDK for Mobius 3D Transfer API - Convert images to 3D models
Downloads
1,199
Maintainers
Readme
@cymmetrik-dev/3d-transfer-sdk
TypeScript SDK for Mobius 3D Transfer API - Convert images to 3D models using AI-powered style transfer.
Features
- Full TypeScript support with auto-generated types
- Support for multiple frameworks (Vite, Next.js, Expo, Node.js)
- Environment variable based configuration
- Axios-based HTTP client
- React ModelViewer component for displaying 3D GLB models
- Default style filtering for single-style deployments
- Additional instruction for custom style guidance (max 2000 chars)
- Add-to-cart redirect for seamless shopping cart integration (supports guest users)
- Split 2D/3D workflow - Control when 3D generation starts with
auto_3dparameter
Installation
npm install @cymmetrik-dev/3d-transfer-sdk
# or
yarn add @cymmetrik-dev/3d-transfer-sdk
# or
pnpm add @cymmetrik-dev/3d-transfer-sdkEnvironment Variables
Configure the SDK using environment variables based on your framework:
| Variable | Framework | Description |
|----------|-----------|-------------|
| VITE_TRANSFER_3D_API_URL | Vite | API base URL |
| VITE_TRANSFER_3D_API_TOKEN | Vite | API authentication token |
| VITE_TRANSFER_3D_DEFAULT_STYLE | Vite | Default style (optional) |
| VITE_DEFAULT_PRODUCT_SLUG | Vite | Default product slug (optional) |
| NEXT_PUBLIC_TRANSFER_3D_API_URL | Next.js | API base URL |
| NEXT_PUBLIC_TRANSFER_3D_API_TOKEN | Next.js | API authentication token |
| NEXT_PUBLIC_TRANSFER_3D_DEFAULT_STYLE | Next.js | Default style (optional) |
| NEXT_PUBLIC_DEFAULT_PRODUCT_SLUG | Next.js | Default product slug (optional) |
| EXPO_PUBLIC_TRANSFER_3D_API_URL | Expo | API base URL |
| EXPO_PUBLIC_TRANSFER_3D_API_TOKEN | Expo | API authentication token |
| EXPO_PUBLIC_TRANSFER_3D_DEFAULT_STYLE | Expo | Default style (optional) |
| EXPO_PUBLIC_DEFAULT_PRODUCT_SLUG | Expo | Default product slug (optional) |
| TRANSFER_3D_API_URL | Node.js | API base URL |
| TRANSFER_3D_API_TOKEN | Node.js | API authentication token |
| TRANSFER_3D_DEFAULT_STYLE | Node.js | Default style (optional) |
| DEFAULT_PRODUCT_SLUG | Node.js | Default product slug (optional) |
Example .env file
# For Vite projects
VITE_TRANSFER_3D_API_URL=https://api.example.com/api
VITE_TRANSFER_3D_API_TOKEN=your-api-token-here
VITE_TRANSFER_3D_DEFAULT_STYLE= # Optional: filter to single style
VITE_DEFAULT_PRODUCT_SLUG= # Optional: default product for accessory info
# For Next.js projects
NEXT_PUBLIC_TRANSFER_3D_API_URL=https://api.example.com/api
NEXT_PUBLIC_TRANSFER_3D_API_TOKEN=your-api-token-here
NEXT_PUBLIC_TRANSFER_3D_DEFAULT_STYLE= # Optional: filter to single style
NEXT_PUBLIC_DEFAULT_PRODUCT_SLUG= # Optional: default product for accessory info
# For Node.js/backend projects
TRANSFER_3D_API_URL=https://api.example.com/api
TRANSFER_3D_API_TOKEN=your-api-token-here
TRANSFER_3D_DEFAULT_STYLE= # Optional: filter to single style
DEFAULT_PRODUCT_SLUG= # Optional: default product for accessory infoQuick Start
Initialize SDK (Recommended)
Use initTransfer3D() to configure the SDK once at app startup. This sets the base URL and token for all subsequent API calls.
import { initTransfer3D } from '@cymmetrik-dev/3d-transfer-sdk';
// Option 1: Auto-detect from environment variables
initTransfer3D();
// Option 2: Explicit configuration
initTransfer3D({
baseURL: 'https://api.example.com/api',
token: 'your-api-token',
});In a Vite/React project, create a setup file:
// src/lib/transfer3d.ts
import { initTransfer3D } from '@cymmetrik-dev/3d-transfer-sdk';
initTransfer3D({
baseURL: import.meta.env.VITE_TRANSFER_3D_API_URL,
token: import.meta.env.VITE_TRANSFER_3D_API_TOKEN,
});Then import it early in your app:
// src/main.tsx
import './lib/transfer3d'; // Initialize SDK
import { createRoot } from 'react-dom/client';
import App from './App';
createRoot(document.getElementById('root')!).render(<App />);Alternative: Create Custom Client
If you need multiple clients (e.g., different API endpoints), use createTransfer3DClient():
import {
createTransfer3DClient,
getTransfer3dStyles,
initiateTransfer3dConversion,
getTransfer3dStatus,
} from '@cymmetrik-dev/3d-transfer-sdk';
// Create a custom client instance
const client = createTransfer3DClient({
baseURL: 'https://api.example.com/api',
token: 'your-api-token',
});
// Pass client to each API call
const styles = await getTransfer3dStyles({ client });Complete Workflow Example
import {
createTransfer3DClient,
getTransfer3dStyles,
initiateTransfer3dConversion,
getTransfer3dStatus,
} from '@cymmetrik-dev/3d-transfer-sdk';
async function convertImageTo3D(imageFile: File) {
// 1. Initialize client
const client = createTransfer3DClient({
token: 'your-api-token',
});
// 2. Get available styles
const stylesResponse = await getTransfer3dStyles({ client });
console.log('Available styles:', stylesResponse.data);
// Output: [{ mode_key: 'smart', mode_name: 'Q版公仔', ... }]
// 3. Upload image and start conversion
const conversionResponse = await initiateTransfer3dConversion({
client,
body: {
image: imageFile,
style: 'smart', // Use a style from step 2
additional_instruction: '增加金色高光效果', // Optional: custom guidance
},
});
const requestId = conversionResponse.data.data.request_id;
console.log('Conversion started:', requestId);
// 4. Poll for status until completed
let status = 'PENDING';
let result = null;
while (status !== 'COMPLETED' && status !== 'FAILED') {
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait 2 seconds
const statusResponse = await getTransfer3dStatus({
client,
path: { requestId },
});
const data = statusResponse.data.data;
status = data.status;
console.log('Status:', status, 'Phase:', data.phase, 'Progress:', data.progress);
// 2D preview is available when phase changes to 3d_generation
if (data.result?.preview_2d_url) {
console.log('2D Preview ready:', data.result.preview_2d_url);
}
if (status === 'COMPLETED') {
result = data.result;
}
}
if (result) {
console.log('3D Model URL:', result.model_3d_url);
console.log('GLB File URL:', result.glb_url);
console.log('2D Preview URL:', result.preview_2d_url);
}
return result;
}Split 2D/3D Workflow (Manual 3D Trigger)
When you want to let users preview the 2D styled image before proceeding to 3D generation:
import {
createTransfer3DClient,
initiateTransfer3dConversion,
triggerTransfer3dConversion,
getTransfer3dStatus,
} from '@cymmetrik-dev/3d-transfer-sdk';
async function convertWithManual3DTrigger(imageFile: File) {
const client = createTransfer3DClient();
// 1. Start 2D-only conversion
const { data } = await initiateTransfer3dConversion({
client,
body: {
image: imageFile,
style: 'smart',
auto_3d: false, // Don't auto-proceed to 3D
},
});
const requestId = data.data.request_id;
// 2. Poll until 2D completes
let status = 'PENDING';
let preview2dUrl = null;
while (status !== '2D_COMPLETED' && status !== 'FAILED') {
await new Promise((r) => setTimeout(r, 2000));
const { data: statusData } = await getTransfer3dStatus({
client,
path: { requestId },
});
status = statusData.data.status;
if (statusData.data.result?.preview_2d_url) {
preview2dUrl = statusData.data.result.preview_2d_url;
}
}
console.log('2D Preview ready:', preview2dUrl);
// At this point, show preview_2d_url to user and wait for confirmation
// 3. When user confirms, trigger 3D generation
await triggerTransfer3dConversion({
client,
body: { request_id: requestId },
});
// 4. Poll until 3D completes
status = 'IN_PROGRESS';
let glbUrl = null;
while (status !== 'COMPLETED' && status !== 'FAILED') {
await new Promise((r) => setTimeout(r, 2000));
const { data: statusData } = await getTransfer3dStatus({
client,
path: { requestId },
});
status = statusData.data.status;
if (status === 'COMPLETED') {
glbUrl = statusData.data.result?.glb_url;
}
}
return { preview2dUrl, glbUrl };
}React Components
ModelViewer
The SDK includes a React component for displaying 3D GLB/GLTF models using Google's model-viewer web component.
import { ModelViewer } from '@cymmetrik-dev/3d-transfer-sdk/react';
function My3DViewer() {
return (
<ModelViewer
src="https://example.com/model.glb"
alt="3D Model"
cameraControls
autoRotate
style={{ width: '100%', height: '400px' }}
/>
);
}ModelViewer Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| src | string | required | URL to the GLB/GLTF model file |
| alt | string | '3D Model' | Alt text for accessibility |
| cameraControls | boolean | true | Enable camera controls (pan, zoom, rotate) |
| autoRotate | boolean | true | Enable auto rotation |
| shadowIntensity | number | 1 | Shadow intensity (0-1) |
| transparentBackground | boolean | false | Enable transparent background (removes default gradient) |
| poster | string | - | Poster image URL to show while loading |
| className | string | - | CSS class name |
| style | CSSProperties | - | Inline styles |
| loading | 'auto' \| 'lazy' \| 'eager' | 'auto' | Loading strategy |
| reveal | 'auto' \| 'manual' | 'auto' | Reveal strategy |
| onLoad | () => void | - | Callback when model is loaded |
| onError | (error: Error) => void | - | Callback when error occurs |
Transparent Background Example
To display the 3D model with a transparent background (useful for overlays or custom backgrounds):
<ModelViewer
src="https://example.com/model.glb"
transparentBackground
cameraControls
autoRotate
style={{ width: '200px', height: '200px' }}
/>Complete React Example with ModelViewer
import { useState, useEffect } from 'react';
import {
createTransfer3DClient,
getTransfer3dStyles,
initiateTransfer3dConversion,
getTransfer3dStatus,
} from '@cymmetrik-dev/3d-transfer-sdk';
import { ModelViewer } from '@cymmetrik-dev/3d-transfer-sdk/react';
export function ImageTo3DConverter() {
const [status, setStatus] = useState<string>('idle');
const [progress, setProgress] = useState(0);
const [preview2dUrl, setPreview2dUrl] = useState<string | null>(null);
const [glbUrl, setGlbUrl] = useState<string | null>(null);
const [webViewUrl, setWebViewUrl] = useState<string | null>(null);
const handleConvert = async (file: File, style: string) => {
const client = createTransfer3DClient();
setStatus('uploading');
const { data } = await initiateTransfer3dConversion({
client,
body: { image: file, style },
});
const requestId = data.data.request_id;
setStatus('processing');
// Poll for completion
const pollStatus = async () => {
const { data: statusData } = await getTransfer3dStatus({
client,
path: { requestId },
});
const result = statusData.data;
setProgress(result.progress || 0);
// Web view URL for viewing on main site
if (result.web_view_url) {
setWebViewUrl(result.web_view_url);
}
// 2D preview becomes available during 3D generation phase
if (result.result?.preview_2d_url) {
setPreview2dUrl(result.result.preview_2d_url);
}
if (result.status === 'COMPLETED') {
setGlbUrl(result.result.glb_url);
setStatus('completed');
} else if (result.status === 'FAILED') {
setStatus('failed');
} else {
setTimeout(pollStatus, 2000);
}
};
pollStatus();
};
return (
<div>
<p>Status: {status} ({progress}%)</p>
{/* Link to view progress on main site */}
{webViewUrl && (
<a href={webViewUrl} target="_blank" rel="noopener noreferrer">
View on main site
</a>
)}
{/* 2D styled preview */}
{preview2dUrl && (
<div>
<h3>2D Style Preview</h3>
<img src={preview2dUrl} alt="2D Preview" />
</div>
)}
{/* 3D Model Viewer */}
{glbUrl && (
<div>
<h3>3D Model</h3>
<ModelViewer
src={glbUrl}
alt="Generated 3D Model"
cameraControls
autoRotate
style={{ width: '100%', height: '400px' }}
/>
</div>
)}
</div>
);
}API Reference
initTransfer3D(config?)
Initialize the default SDK client. Call this once at app startup to configure the base URL and authentication token for all API calls.
Parameters:
config.baseURL(optional): API base URL. Falls back to environment variables.config.token(optional): Bearer authentication token. Falls back to environment variables.
Example:
import { initTransfer3D, getTransfer3dStyles } from '@cymmetrik-dev/3d-transfer-sdk';
// Initialize with explicit config
initTransfer3D({
baseURL: 'https://api.example.com/api',
token: 'your-api-token',
});
// Or auto-detect from environment variables
initTransfer3D();
// Now all API calls use this configuration (no need to pass client)
const styles = await getTransfer3dStyles();createTransfer3DClient(config?)
Create a separate API client instance. Use this when you need multiple clients or want to override the default configuration for specific calls.
Parameters:
config.baseURL(optional): API base URLconfig.token(optional): Bearer authentication tokenconfig.timeout(optional): Request timeout in ms (default: 30000)
Returns: Configured axios client
getTransfer3dStyles(options)
Get available 3D transfer styles.
Parameters:
query.default_style(optional): string - Filter to return only this style
Example:
// Get all styles
const allStyles = await getTransfer3dStyles({ client });
// Get single style (useful for single-style deployments)
const singleStyle = await getTransfer3dStyles({
client,
query: { default_style: 'smart' },
});Returns:
{
success: true,
data: Array<{
mode_key: string; // Style identifier (e.g., 'smart')
mode_name: string; // Display name (e.g., 'Q版公仔')
mode_description: string;
preview_image_url: string | null;
}>
}Note: If
default_styleis specified but not found, API returns all styles as fallback.
initiateTransfer3dConversion(options)
Upload an image and start the 3D conversion process.
Parameters:
body.image: File - Image to convert (max 10MB, supported: jpg, png, webp)body.style: string - Style key fromgetTransfer3dStyles()body.additional_instruction(optional): string - Custom style guidance (max 2000 chars)body.auto_3d(optional): boolean - Auto-proceed to 3D after 2D completes (default:true)
Example:
// Basic conversion (auto 2D → 3D)
const result = await initiateTransfer3dConversion({
client,
body: {
image: imageFile,
style: 'smart',
},
});
// With additional instruction for fine-tuning
const result = await initiateTransfer3dConversion({
client,
body: {
image: imageFile,
style: 'smart',
additional_instruction: '增加金色高光效果,使用暖色調',
},
});
// 2D-only mode: Stop after 2D style transfer
const result = await initiateTransfer3dConversion({
client,
body: {
image: imageFile,
style: 'smart',
auto_3d: false, // Will stop at 2D_COMPLETED status
},
});Additional Instruction Use Cases:
- Fine-tune style output (e.g., "更卡通化", "增加質感")
- Specify colors or materials (e.g., "使用暖色調", "金屬質感")
- Adjust lighting effects (e.g., "增加高光", "柔和陰影")
Note:
additional_instructionis appended to the style's base instructions, not replaced.
Returns:
{
success: true,
data: {
request_id: string; // UUID for status polling
chat_thread_id: number; // Internal ChatThread ID (for add-to-cart)
status: 'PENDING';
message: string;
}
}triggerTransfer3dConversion(options)
Trigger 3D generation for a request that completed 2D styling. Use this when you initiated a conversion with auto_3d: false.
Parameters:
body.request_id: string - The request ID from the original/convertcall
Example:
// After status shows '2D_COMPLETED', trigger 3D generation
const result = await triggerTransfer3dConversion({
client,
body: {
request_id: 'uuid-xxx',
},
});Returns:
{
success: true,
data: {
request_id: string;
chat_thread_id: number; // Internal ChatThread ID (for add-to-cart)
status: 'IN_PROGRESS';
message: '3D conversion initiated';
}
}Error Responses:
400- 2D style transfer not yet completed400- 3D conversion already in progress or completed404- Request not found
getTransfer3dStatus(options)
Get the status of a conversion request.
Parameters:
path.requestId: string - Request ID from conversion response
Returns:
{
success: true,
data: {
request_id: string;
status: 'PENDING' | 'IN_PROGRESS' | '2D_COMPLETED' | 'COMPLETED' | 'FAILED';
phase: '2d_style_transfer' | '3d_generation_pending' | '3d_generation';
progress: number; // 0-100 (2D_COMPLETED = 50%)
web_view_url: string; // URL to view progress on main site
auto_3d?: boolean; // Present for API requests, indicates if 3D will auto-start
result?: {
preview_2d_url: string; // Available when 2D transfer completes
model_3d_url: string; // Available when fully completed
glb_url: string; // GLB file URL (same as model_3d_url)
};
error?: string; // Present if status is FAILED
}
}Note: When
auto_3d: false, the status will be2D_COMPLETED(notCOMPLETED) when 2D styling finishes. CalltriggerTransfer3dConversion()to start 3D generation.
getTransfer3DProduct(options)
Get product information by slug, including name, description, price, and images.
Parameters:
path.slug: string - The product slug
Example:
import {
createTransfer3DClient,
getTransfer3DProduct,
getDefaultProductSlug,
} from '@cymmetrik-dev/3d-transfer-sdk';
const client = createTransfer3DClient();
// Get product by slug
const { data } = await getTransfer3DProduct({
client,
path: { slug: 'my-product-slug' },
});
console.log('Product:', data.data.name);
console.log('Price:', data.data.price?.formatted);
console.log('Main Image:', data.data.main_image_url);
// Or use default product slug from environment
const defaultSlug = getDefaultProductSlug();
if (defaultSlug) {
const { data } = await getTransfer3DProduct({
client,
path: { slug: defaultSlug },
});
}Returns:
{
success: true,
data: {
slug: string;
name: string;
description: string;
price: {
value: number; // Price in smallest currency unit
formatted: string; // e.g., "NT$1,990"
currency: string; // e.g., "TWD"
} | null;
main_image_url: string | null;
images: Array<{
url: string;
alt: string;
}>;
variants: Array<{
id: number; // Variant ID (use for add-to-cart)
sku: string; // SKU code
options: Array<{ // Variant options (e.g., color, size)
name: string; // Option name (e.g., "顏色")
value: string; // Option value (e.g., "紅色")
}>;
price: {
value: number;
formatted: string;
currency: string;
};
}>;
}
}Example with Variants:
const { data } = await getTransfer3DProduct({
client,
path: { slug: 'my-product' },
});
// Display product with variant selector
console.log('Product:', data.data.name);
data.data.variants.forEach((variant) => {
const optionText = variant.options
.map(o => `${o.name}: ${o.value}`)
.join(', ');
console.log(`- ${variant.sku} (${optionText}): ${variant.price.formatted}`);
});
// Add specific variant to cart
const selectedVariant = data.data.variants[0];
const cartUrl = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'product_variant', model_id: selectedVariant.id }],
});getDefaultProductSlug()
Get the default product slug from environment variables.
Returns: string | undefined
Example:
import { getDefaultProductSlug } from '@cymmetrik-dev/3d-transfer-sdk';
const slug = getDefaultProductSlug();
// Returns value from VITE_DEFAULT_PRODUCT_SLUG, NEXT_PUBLIC_DEFAULT_PRODUCT_SLUG, etc.getDefaultStyle()
Get the default style from environment variables.
Returns: string | undefined
Example:
import { getDefaultStyle } from '@cymmetrik-dev/3d-transfer-sdk';
const style = getDefaultStyle();
// Returns value from VITE_TRANSFER_3D_DEFAULT_STYLE, etc.Add to Cart Redirect
The SDK provides helper functions to build URLs that add products to the shopping cart and redirect users. This feature:
- Works for both logged-in users and guest users
- Supports adding multiple products at once
- Allows custom redirect URLs after adding to cart
- No authentication token required (public endpoint)
buildAddToCartUrl(baseURL, options)
Build a URL that adds products to cart and redirects.
Parameters:
baseURL: string - The shop's base URL (without/api)options.products: CartProduct[] - Products to addoptions.redirectUrl: string (optional) - Redirect URL after adding (default:/cart)
CartProduct interface:
interface CartProduct {
model_type:
| 'Lunar\\Models\\ProductVariant'
| 'Projects\\Frontier\\Models\\FrontierOrder'
| 'Projects\\Frontier\\Models\\ChatThread'; // Auto-creates FrontierOrder
model_id: number;
quantity?: number; // default: 1
}Example:
import { buildAddToCartUrl } from '@cymmetrik-dev/3d-transfer-sdk';
// Add single product
const url = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'product_variant', model_id: 123 }],
});
// Result: https://shop.example.com/api/3d-transfer/add-to-cart?products=[{"model_type":"product_variant","model_id":123}]
// Add multiple products with quantities
const url = buildAddToCartUrl('https://shop.example.com', {
products: [
{ model_type: 'product_variant', model_id: 123, quantity: 2 },
{ model_type: 'product_variant', model_id: 456, quantity: 1 },
],
});
// With custom redirect URL
const url = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'product_variant', model_id: 123 }],
redirectUrl: '/checkout',
});
// Add 3D model by ChatThread ID (auto-creates FrontierOrder if needed)
const url = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'Projects\\Frontier\\Models\\ChatThread', model_id: chatThreadId }],
});
// Or add existing FrontierOrder directly
const url = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'Projects\\Frontier\\Models\\FrontierOrder', model_id: 789 }],
});buildAddToCartUrlFromEnv(options)
Build add-to-cart URL using the API URL from environment variables.
Parameters:
options.products: CartProduct[] - Products to addoptions.redirectUrl: string (optional) - Redirect URL after adding
Returns: string | null - URL or null if API URL not configured
Example:
import { buildAddToCartUrlFromEnv } from '@cymmetrik-dev/3d-transfer-sdk';
const url = buildAddToCartUrlFromEnv({
products: [{ model_type: 'product_variant', model_id: 123 }],
});
if (url) {
window.location.href = url;
}Add to Cart - React Example
import { buildAddToCartUrl, getTransfer3DProduct } from '@cymmetrik-dev/3d-transfer-sdk';
function BuyButton({ productId }: { productId: number }) {
const handleBuy = () => {
const url = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'product_variant', model_id: productId }],
redirectUrl: '/checkout',
});
window.location.href = url;
};
return (
<button onClick={handleBuy}>
加入購物車
</button>
);
}
// Or use as a link
function BuyLink({ productId }: { productId: number }) {
const url = buildAddToCartUrl('https://shop.example.com', {
products: [{ model_type: 'product_variant', model_id: productId }],
});
return (
<a href={url}>
加入購物車
</a>
);
}Add to Cart - HTML Example
<!-- Direct link to add product to cart -->
<a href="https://shop.example.com/api/3d-transfer/add-to-cart?products=[{"model_type":"product_variant","model_id":123}]">
加入購物車
</a>
<!-- With redirect to checkout -->
<a href="https://shop.example.com/api/3d-transfer/add-to-cart?products=[{"model_type":"product_variant","model_id":123}]&redirect_url=/checkout">
立即購買
</a>Complete Workflow: 3D Model to Cart
import {
createTransfer3DClient,
initiateTransfer3dConversion,
getTransfer3dStatus,
buildAddToCartUrl,
} from '@cymmetrik-dev/3d-transfer-sdk';
// Full workflow: Upload image → Convert → Add to cart
async function convertAndAddToCart(imageFile: File) {
const client = createTransfer3DClient();
// 1. Start conversion and get chat_thread_id
const { data: conversionData } = await initiateTransfer3dConversion({
client,
body: { image: imageFile, style: 'smart' },
});
const { request_id: requestId, chat_thread_id: chatThreadId } = conversionData.data;
console.log('ChatThread ID:', chatThreadId); // Use this for add-to-cart
// 2. Poll until completed
let status = 'PENDING';
while (status !== 'COMPLETED' && status !== 'FAILED') {
await new Promise((r) => setTimeout(r, 2000));
const { data } = await getTransfer3dStatus({ client, path: { requestId } });
status = data.data.status;
}
// 3. Add to cart using ChatThread ID (auto-creates FrontierOrder)
if (status === 'COMPLETED') {
const cartUrl = buildAddToCartUrl('https://shop.example.com', {
products: [{
model_type: 'Projects\\Frontier\\Models\\ChatThread',
model_id: chatThreadId,
}],
});
window.location.href = cartUrl;
}
}Conversion Phases
| Phase | Progress | Description |
|-------|----------|-------------|
| 2d_style_transfer | 0-50% | Image is being styled with AI |
| 3d_generation_pending | 50% | 2D complete, waiting for 3D to start |
| 3d_generation | 50-100% | 3D model is being generated |
Note: The preview_2d_url becomes available when the phase changes to 3d_generation_pending or 3d_generation, allowing you to show the styled image while the 3D model is still being generated.
Status Values
| Status | Description |
|--------|-------------|
| PENDING | Conversion request queued |
| IN_PROGRESS | Currently processing (2D or 3D) |
| 2D_COMPLETED | 2D styling finished, waiting for 3D trigger (only when auto_3d: false) |
| COMPLETED | Full conversion finished (2D + 3D) |
| FAILED | An error occurred |
Tip: For
auto_3d: falserequests, poll untilstatus === '2D_COMPLETED', show thepreview_2d_url, then calltriggerTransfer3dConversion()when ready.
TypeScript Types
import type {
// Request/Response types (auto-generated)
GetTransfer3dStylesResponse,
InitiateTransfer3dConversionData,
InitiateTransfer3dConversionResponse,
TriggerTransfer3dConversionData,
TriggerTransfer3dConversionResponse,
GetTransfer3dStatusData,
GetTransfer3dStatusResponse,
GetTransfer3DProductData,
GetTransfer3DProductResponse,
// Add to cart types
CartProduct,
AddToCartOptions,
} from '@cymmetrik-dev/3d-transfer-sdk';
// Helper functions
import {
getDefaultProductSlug,
getDefaultStyle,
buildAddToCartUrl,
buildAddToCartUrlFromEnv,
} from '@cymmetrik-dev/3d-transfer-sdk';
// API functions
import {
getTransfer3dStyles,
initiateTransfer3dConversion,
triggerTransfer3dConversion,
getTransfer3dStatus,
getTransfer3DProduct,
} from '@cymmetrik-dev/3d-transfer-sdk';
// React component types
import type { ModelViewerProps } from '@cymmetrik-dev/3d-transfer-sdk/react';Error Handling
import { AxiosError } from 'axios';
try {
const response = await initiateTransfer3dConversion({
client,
body: { image: file, style: 'smart' },
});
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status === 401) {
console.error('Authentication failed. Check your API token.');
} else if (error.response?.status === 422) {
console.error('Validation error:', error.response.data.errors);
} else if (error.response?.status === 429) {
console.error('Rate limit exceeded. Please try again later.');
}
}
throw error;
}Demo Application
A complete React demo application is available in the examples/react-demo directory.
To run the demo:
cd examples/react-demo
pnpm install
cp .env.example .env # Configure your API URL and token
pnpm devSupport
This SDK is auto-generated from the Mobius Frontier OpenAPI specification using @hey-api/openapi-ts
