Understanding Mockup API Data Models: A Technical Guide
In a SudoMock upload response, each smart object's uuid is the handle you send in a render request, size is the resolution to prepare artwork at, position is its box on the mockup canvas, and quad holds its four corner points. Design at size, preview with position, and the render scales your artwork into place.
Published Updated 4 min readBeck

Understanding how mockup APIs structure their data is crucial for building robust integrations. This guide covers the de-facto standard data model used across the industry, what each field means, and how to use them effectively in your code.
The De-Facto Standard: Rect + Transform
Every major design tool and mockup API uses the same fundamental data model: a rectangle with position coordinates and optional transforms. This pattern comes from decades of graphics programming, from Photoshop to Canvas2D to WebGL.
x, y
Position
Top-left corner coordinates
w, h
Size
Width and height dimensions
deg
Rotation
Optional rotation angle
quad
Transform
4-point perspective
This model is universal because it's deterministic, minimal, and fast. Whether you're working with Photoshop layers, Figma frames, or mockup APIs, the core concept remains the same: define a bounding box and optionally transform it.
Anatomy of a Smart Object Response
When you upload a PSD to SudoMock, each smart object layer returns detailed metadata. Let's break down what each field means and how to use it:
{"uuid": "73957ff7-82c4-4c14-947a-a6f841944d1c","name": "print_area","size": {"width": 3951,"height": 4800},"position": {"x": 771,"y": 886,"width": 507,"height": 620},"quad": [[771, 886],[1278, 886],[1278, 1506],[771, 1506]],"blend_mode": "normal","layer_name": "print_area"}
Understanding Each Field
- size (width, height): The original dimensions of the embedded smart object content. This is your design canvas size - upload assets at this resolution for best quality. The render API works in these coordinates.
- position.x, position.y: Top-left corner placement coordinates on the mockup canvas. Useful for client-side canvas preview overlays.
- position.width, position.height: Display dimensions after transforms. Primary use: drawing design previews on thumbnail images in browser. Not used in render API requests.
- quad (4 points): Pre-calculated corner coordinates for perspective rendering. Useful for canvas-based rendering or custom transform implementations.
- blend_mode: How this layer blends with layers below. Important for color overlays (multiply) or special effects (screen, overlay).
size vs position: The Key Distinction
The most important concept to understand is the difference between size and position.
They represent two different things:
| Field | Represents | Use Case |
|---|---|---|
| size.width | Embedded content width (3951px) | Render API coordinates, design canvas |
| size.height | Embedded content height (4800px) | Render API coordinates, design canvas |
| position.x | Left edge on mockup (771px) | Client-side canvas preview |
| position.y | Top edge on mockup (886px) | Client-side canvas preview |
| position.width | Display width (507px) | Thumbnail overlay drawing |
| position.height | Display height (620px) | Thumbnail overlay drawing |
In the example above, a 3951x4800 design gets scaled down to 507x620 when rendered on the mockup canvas. The scale factor is approximately 0.128x (507/3951).
Working with Quad Coordinates
The quad array contains four [x, y] coordinate pairs representing the corners
of the smart object's bounding box. This is especially useful for:
- Canvas rendering: Draw directly using canvas 2D context with precise coordinates
- Perspective transforms: Handle non-rectangular smart objects (skewed, rotated)
- Hit testing: Determine if a point is inside the smart object area
- Custom overlays: Position UI elements or annotations accurately
// Draw smart object bounds on canvasfunction drawSmartObjectBounds(ctx, smartObject) {if (!smartObject.quad) return; // quad can be nullconst [topLeft, topRight, bottomRight, bottomLeft] = smartObject.quad;ctx.beginPath();ctx.moveTo(topLeft[0], topLeft[1]);ctx.lineTo(topRight[0], topRight[1]);ctx.lineTo(bottomRight[0], bottomRight[1]);ctx.lineTo(bottomLeft[0], bottomLeft[1]);ctx.closePath();ctx.strokeStyle = '#00ff00';ctx.stroke();}// Check if a point is inside the smart objectfunction isPointInQuad(point, quad) {const [tl, tr, br, bl] = quad;// Simple bounding box check (for rectangular quads)return point.x >= tl[0] && point.x <= tr[0] &&point.y >= tl[1] && point.y <= bl[1];}
Integration Patterns
- 1Upload PSD/psd/upload
- 2Get Metadatasize, position, quad
- 3Render/renders
- 4ResultWebP/PNG URL
Pattern 1: Basic Render Request
The most common pattern is uploading a PSD, storing the smart object metadata, then using it to render variations:
// Step 1: Upload PSD and store metadataconst uploadResponse = await fetch('https://api.sudomock.com/api/v1/psd/upload', {method: 'POST',headers: {'X-API-KEY': API_KEY,'Content-Type': 'application/json'},body: JSON.stringify({psd_file_url: 'https://storage.example.com/tshirt-mockup.psd',psd_name: 'T-Shirt Front'})});const { data: mockup } = await uploadResponse.json();// Store the mockup metadata for later useconst mockupId = mockup.uuid;const smartObjects = mockup.smart_objects;// Find the main design areaconst designArea = smartObjects.find(so => so.name === 'print_area');console.log('Design canvas size:', designArea.size); // { width: 3951, height: 4800 }console.log('Rendered position:', designArea.position); // { x: 771, y: 886, width: 507, height: 620 }// Step 2: Render with a designconst renderResponse = await fetch('https://api.sudomock.com/api/v1/renders', {method: 'POST',headers: {'X-API-KEY': API_KEY,'Content-Type': 'application/json'},body: JSON.stringify({mockup_uuid: mockupId,smart_objects: [{uuid: designArea.uuid,asset: {url: 'https://storage.example.com/my-design.png',fit: 'cover' // cover, contain, or fill}}],export_options: {image_format: 'webp',image_size: 1024,quality: 95}})});const { data: render } = await renderResponse.json();console.log('Rendered mockup:', render.print_files[0].export_path);
Pattern 2: Using Position Data for Custom Rendering
If you're building a custom preview or need to render locally, the position data tells you exactly where to place the design:
async function createPreview(mockupImage, designImage, smartObject) {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');// Set canvas to mockup dimensionscanvas.width = mockupImage.width;canvas.height = mockupImage.height;// Draw the mockup backgroundctx.drawImage(mockupImage, 0, 0);// Get position data from API responseconst { x, y, width, height } = smartObject.position;// Draw the design at the correct position and sizectx.drawImage(designImage, x, y, width, height);return canvas.toDataURL('image/png');}// Usageconst preview = await createPreview(await loadImage(mockupThumbnail),await loadImage(myDesign),designArea);
Working with Blend Modes
The blend_mode field indicates how the smart object layer blends with
layers below it in the PSD. Common modes include:
| Blend Mode | Effect | Common Use |
|---|---|---|
| NORMAL | Standard opacity blending | Regular design placement |
| MULTIPLY | Darkens, whites become transparent | Color overlays, fabric textures |
| SCREEN | Lightens, blacks become transparent | Light effects, glows |
| OVERLAY | Combines multiply and screen | Contrast enhancement |
For color smart objects (used to change product colors), MULTIPLY is
typically used so the design texture shows through the color overlay. This is common in drinkware mockups and apparel templates.
Next.js Integration Examples
Here are practical examples for integrating SudoMock into your Next.js application. You can use any template from SudoMock's mockup library as a starting point. These examples show how to use the smart object metadata in real frontend code.
SudoMock Client Utility
First, create a reusable client for making API calls to SudoMock:
// SudoMock API Client for Next.js// Store your API key in .env.local as SUDOMOCK_API_KEYconst API_KEY = process.env.SUDOMOCK_API_KEY!const BASE_URL = 'https://api.sudomock.com/api/v1'export interface SmartObject {uuid: stringname: stringsize: { width: number; height: number }position: { x: number; y: number; width: number; height: number }quad: [number, number][] | nullblend_mode: string}export interface MockupData {uuid: stringname: stringthumbnail: stringwidth?: number // Original PSD widthheight?: number // Original PSD heightsmart_objects: SmartObject[]}// Upload a PSD and get smart object metadataexport async function uploadPsd(psdUrl: string, name: string): Promise<MockupData> {const response = await fetch(`${BASE_URL}/psd/upload`, {method: 'POST',headers: {'X-API-KEY': API_KEY,'Content-Type': 'application/json',},body: JSON.stringify({psd_file_url: psdUrl,psd_name: name,}),})const data = await response.json()if (!data.success) throw new Error(data.detail)return data.data}// Render a mockup with a designexport async function renderMockup(mockupId: string,smartObjectId: string,designUrl: string): Promise<string> {const response = await fetch(`${BASE_URL}/renders`, {method: 'POST',headers: {'X-API-KEY': API_KEY,'Content-Type': 'application/json',},body: JSON.stringify({mockup_uuid: mockupId,smart_objects: [{uuid: smartObjectId,asset: { url: designUrl, fit: 'cover' }}],export_options: {image_format: 'webp',image_size: 1024,quality: 95}}),})const data = await response.json()if (!data.success) throw new Error(data.detail)return data.data.print_files[0].export_path}
React Hook for Managing Mockups
A custom hook that manages mockup state and provides useful computed values from the smart object metadata:
export function useMockupEditor(initialMockup?: MockupData) {const [mockup, setMockup] = useState<MockupData | null>(initialMockup ?? null)const [selectedDesign, setSelectedDesign] = useState<string | null>(null)// Find the main print area smart objectconst printArea = useMemo(() => {return mockup?.smart_objects.find(so => so.name.toLowerCase().includes('print') ||so.name.toLowerCase().includes('design'))}, [mockup])// Calculate design requirements from size fieldconst designRequirements = useMemo(() => {if (!printArea) return nullconst { width, height } = printArea.sizereturn {width,height,aspectRatio: width / height,megapixels: (width * height) / 1000000,recommendation: width >= 3000? 'High resolution - perfect for print': 'Medium resolution - good for web'}}, [printArea])// Get render position info from position fieldconst renderPosition = useMemo(() => {if (!printArea) return nullconst { x, y, width, height } = printArea.positionconst originalSize = printArea.sizereturn {x,y,width,height,scaleFactor: width / originalSize.width,// Useful for canvas previewbounds: { left: x, top: y, right: x + width, bottom: y + height }}}, [printArea])return {mockup,setMockup,printArea,designRequirements,renderPosition,selectedDesign,setSelectedDesign,}}
Live Preview with Canvas
Use the position data to create a real-time preview before rendering:
interface PreviewProps {mockupThumbnailUrl: stringdesignImageUrl: string | null// From smart_object.position in API responseposition: { x: number; y: number; width: number; height: number }}export function MockupPreview({ mockupThumbnailUrl, designImageUrl, position }: PreviewProps) {const canvasRef = useRef<HTMLCanvasElement>(null)const [previewDataUrl, setPreviewDataUrl] = useState<string | null>(null)useEffect(() => {if (!designImageUrl || !canvasRef.current) returnconst canvas = canvasRef.currentconst ctx = canvas.getContext('2d')if (!ctx) return// Load both imagesconst mockupImg = new Image()const designImg = new Image()mockupImg.crossOrigin = 'anonymous'designImg.crossOrigin = 'anonymous'Promise.all([new Promise<void>(resolve => { mockupImg.onload = () => resolve(); mockupImg.src = mockupThumbnailUrl }),new Promise<void>(resolve => { designImg.onload = () => resolve(); designImg.src = designImageUrl }),]).then(() => {// Set canvas to mockup sizecanvas.width = mockupImg.widthcanvas.height = mockupImg.height// Draw mockup backgroundctx.drawImage(mockupImg, 0, 0)// Draw design at the exact position from API// The position object tells us exactly where to place it!ctx.drawImage(designImg,position.x, // x coordinate from APIposition.y, // y coordinate from APIposition.width, // rendered width from APIposition.height // rendered height from API)setPreviewDataUrl(canvas.toDataURL('image/png'))})}, [mockupThumbnailUrl, designImageUrl, position])return (<div className="relative aspect-square bg-slate-800 rounded-lg overflow-hidden"><canvas ref={canvasRef} className="hidden" />{previewDataUrl && (<img src={previewDataUrl} alt="Preview" className="w-full h-full object-contain" />)}</div>)}
Server Action for Batch Rendering
Process multiple designs in parallel using Next.js Server Actions:
'use server'interface BatchResult {designUrl: stringrenderUrl: string | nullsuccess: booleanerror?: string}export async function batchRenderMockups(mockupId: string,smartObjectId: string,designUrls: string[]): Promise<BatchResult[]> {// Process 5 at a time to respect rate limitsconst CONCURRENCY = 5const results: BatchResult[] = []for (let i = 0; i < designUrls.length; i += CONCURRENCY) {const batch = designUrls.slice(i, i + CONCURRENCY)const batchResults = await Promise.all(batch.map(async (designUrl): Promise<BatchResult> => {try {const renderUrl = await renderMockup(mockupId, smartObjectId, designUrl)return { designUrl, renderUrl, success: true }} catch (error) {return {designUrl,renderUrl: null,success: false,error: error instanceof Error ? error.message : 'Unknown error'}}}))results.push(...batchResults)}return results}
Plans start at $25/month. Subscriptions from $0.002 per render.
Related Resources
Complete documentation for the upload endpoint
All render options and parameters
Best practices for preparing PSD templates
Status codes, error codes and safe retries
Frequently asked questions
What is a mockup data model?
A mockup data model defines the structure of your mockup configuration - including Smart Object positions, layer names, transformation settings, and output parameters. It's essentially a blueprint that tells the API how to process your PSD template.
How do I identify Smart Object layers in my PSD?
Smart Object layers show a special icon in the Photoshop Layers panel. Through the API, the upload response and GET /api/v1/psd-mockups/{uuid} list every Smart Object of the template with its uuid, name and position. The uuid is what a render request sends.
Can I update multiple Smart Objects in one API call?
Yes. A render request lists several Smart Objects in its smart_objects array, each with the uuid of the Smart Object and the URL of the design to place in it. Every Smart Object in the template can take its own design in the same render.
What happens if a Smart Object uuid does not match?
The render is refused with HTTP 400 and the error code SMART_OBJECT_NOT_FOUND. The error lists the Smart Objects the template has, each with its uuid and name, so the request can be corrected. Read the uuids from the upload response or from GET /api/v1/psd-mockups/{uuid} before rendering.
How do transformations affect the inserted design?
Transformations (scale, rotation, perspective) applied to the Smart Object in Photoshop are automatically applied to any content inserted via the API. This ensures your designs appear with the same realistic warping and positioning as seen in the original template.
Beck
CTO
Related articles

PSD Mockup Template Guide: Best Practices for Automation
Learn how to create API-ready PSD mockup templates. Complete guide covering dimensions, Smart Object setup, layer naming, blend modes, and optimization for automation.
7 min read

SudoMock Now Works with Claude, Cursor, ChatGPT, and Any MCP-Compatible AI Tool
Render product mockups directly from any AI assistant that supports MCP. Claude, Cursor, VS Code, ChatGPT, Windsurf, and more.
7 min read

Every T-Shirt Color Variant Mockup from One PSD by API
One PSD with a color smart object renders every t-shirt colorway: one request per color, shown with six real renders.
4 min read
Explore by use case
See SudoMock in action for your specific product category
Your own PSD, rendered via API
Render your own PSD through the API. Start with 500 one-time credits, no card.