Dec 5, 202515 minSudoMock Team

Understanding Mockup API Data Models: A Technical Guide

Deep dive into mockup API response structures. Learn how smart object data is organized, what each field means, and how to build robust integrations.

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.
size vs position - Visual Explanation
size
3951 x 4800
Your design canvas
4800px
3951px
Design Resolution
Upload assets at this size
scales to
position
507 x 620
x: 771, y: 886
Mockup Canvas
Rendered Position
Where it appears on mockup
size = Original design dimensions
position = Rendered bounds on canvas

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:

Smart Object Response Structure
json
1{
2 "uuid": "73957ff7-82c4-4c14-947a-a6f841944d1c",
3 "name": "print_area",
4 "size": {
5 "width": 3951,
6 "height": 4800
7 },
8 "position": {
9 "x": 771,
10 "y": 886,
11 "width": 507,
12 "height": 620
13 },
14 "quad": [
15 [771, 886],
16 [1278, 886],
17 [1278, 1506],
18 [771, 1506]
19 ],
20 "blend_mode": "BlendMode.NORMAL",
21 "layer_name": "print_area"
22}

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.
position (x, y, width, height)The rendered bounding box on the mockup canvas. x/y is the top-left corner, width/height is the display size after any transforms.
quad (4 points)Pre-calculated corner coordinates for perspective rendering. Useful for canvas-based rendering or custom transform implementations.
blend_modeHow 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:

FieldRepresentsUse Case
size.widthEmbedded content width (3951px)Design canvas resolution
size.heightEmbedded content height (4800px)Design canvas resolution
position.xLeft edge on mockup (771px)Placement coordinate
position.yTop edge on mockup (886px)Placement coordinate
position.widthDisplay width (507px)Rendered size after transform
position.heightDisplay height (620px)Rendered size after transform

Think of it this way

size = "Upload your design at this resolution"
position = "Your design will appear here on the mockup"

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 renderingDraw directly using canvas 2D context with precise coordinates
Perspective transformsHandle non-rectangular smart objects (skewed, rotated)
Hit testingDetermine if a point is inside the smart object area
Custom overlaysPosition UI elements or annotations accurately
Using quad coordinates in JavaScript
javascript
1// Draw smart object bounds on canvas
2function drawSmartObjectBounds(ctx, smartObject) {
3 const [topLeft, topRight, bottomRight, bottomLeft] = smartObject.quad;
4
5 ctx.beginPath();
6 ctx.moveTo(topLeft[0], topLeft[1]);
7 ctx.lineTo(topRight[0], topRight[1]);
8 ctx.lineTo(bottomRight[0], bottomRight[1]);
9 ctx.lineTo(bottomLeft[0], bottomLeft[1]);
10 ctx.closePath();
11 ctx.strokeStyle = '#00ff00';
12 ctx.stroke();
13}
14
15// Check if a point is inside the smart object
16function isPointInQuad(point, quad) {
17 const [tl, tr, br, bl] = quad;
18 // Simple bounding box check (for rectangular quads)
19 return point.x >= tl[0] && point.x <= tr[0] &&
20 point.y >= tl[1] && point.y <= bl[1];
21}

Integration Patterns

Integration Flow
📤
Upload PSD
/psd/upload
📋
Get Metadata
size, position, quad
🎨
Render
/renders
🖼️
Result
WebP/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:

Complete integration flow
javascript
1// Step 1: Upload PSD and store metadata
2const uploadResponse = await fetch('https://api.sudomock.com/api/v1/psd/upload', {
3 method: 'POST',
4 headers: {
5 'X-API-KEY': API_KEY,
6 'Content-Type': 'application/json'
7 },
8 body: JSON.stringify({
9 psd_file_url: 'https://storage.example.com/tshirt-mockup.psd',
10 psd_name: 'T-Shirt Front'
11 })
12});
13
14const { data: mockup } = await uploadResponse.json();
15
16// Store the mockup metadata for later use
17const mockupId = mockup.uuid;
18const smartObjects = mockup.smart_objects;
19
20// Find the main design area
21const designArea = smartObjects.find(so => so.name === 'print_area');
22console.log('Design canvas size:', designArea.size); // { width: 3951, height: 4800 }
23console.log('Rendered position:', designArea.position); // { x: 771, y: 886, width: 507, height: 620 }
24
25// Step 2: Render with a design
26const renderResponse = await fetch('https://api.sudomock.com/api/v1/renders', {
27 method: 'POST',
28 headers: {
29 'X-API-KEY': API_KEY,
30 'Content-Type': 'application/json'
31 },
32 body: JSON.stringify({
33 mockup_uuid: mockupId,
34 smart_objects: [{
35 uuid: designArea.uuid,
36 asset: {
37 url: 'https://storage.example.com/my-design.png',
38 fit: 'cover' // cover, contain, or fill
39 }
40 }],
41 export_options: {
42 image_format: 'webp',
43 image_size: 1920,
44 quality: 95
45 }
46 })
47});
48
49const { data: render } = await renderResponse.json();
50console.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:

Custom canvas preview
javascript
1async function createPreview(mockupImage, designImage, smartObject) {
2 const canvas = document.createElement('canvas');
3 const ctx = canvas.getContext('2d');
4
5 // Set canvas to mockup dimensions
6 canvas.width = mockupImage.width;
7 canvas.height = mockupImage.height;
8
9 // Draw the mockup background
10 ctx.drawImage(mockupImage, 0, 0);
11
12 // Get position data from API response
13 const { x, y, width, height } = smartObject.position;
14
15 // Draw the design at the correct position and size
16 ctx.drawImage(designImage, x, y, width, height);
17
18 return canvas.toDataURL('image/png');
19}
20
21// Usage
22const preview = await createPreview(
23 await loadImage(mockupThumbnail),
24 await loadImage(myDesign),
25 designArea
26);

Design Resolution

For best quality, prepare your design at the size dimensions (e.g., 3951x4800), not the position dimensions. The API handles scaling automatically.

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 ModeEffectCommon Use
NORMALStandard opacity blendingRegular design placement
MULTIPLYDarkens, whites become transparentColor overlays, fabric textures
SCREENLightens, blacks become transparentLight effects, glows
OVERLAYCombines multiply and screenContrast enhancement

For color smart objects (used to change product colors), MULTIPLY is typically used so the design texture shows through the color overlay.


Next.js Integration Examples

Here are practical examples for integrating SudoMock into your Next.js application. 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:

lib/sudomock.ts
typescript
1// SudoMock API Client for Next.js
2// Store your API key in .env.local as SUDOMOCK_API_KEY
3
4const API_KEY = process.env.SUDOMOCK_API_KEY!
5const BASE_URL = 'https://api.sudomock.com/api/v1'
6
7export interface SmartObject {
8 uuid: string
9 name: string
10 size: { width: number; height: number }
11 position: { x: number; y: number; width: number; height: number }
12 quad: [number, number][]
13 blend_mode: string
14}
15
16export interface MockupData {
17 uuid: string
18 name: string
19 thumbnail: string
20 smart_objects: SmartObject[]
21}
22
23// Upload a PSD and get smart object metadata
24export async function uploadPsd(psdUrl: string, name: string): Promise<MockupData> {
25 const response = await fetch(`${BASE_URL}/psd/upload`, {
26 method: 'POST',
27 headers: {
28 'X-API-KEY': API_KEY,
29 'Content-Type': 'application/json',
30 },
31 body: JSON.stringify({
32 psd_file_url: psdUrl,
33 psd_name: name,
34 }),
35 })
36
37 const data = await response.json()
38 if (!data.success) throw new Error(data.detail)
39 return data.data
40}
41
42// Render a mockup with a design
43export async function renderMockup(
44 mockupId: string,
45 smartObjectId: string,
46 designUrl: string
47): Promise<string> {
48 const response = await fetch(`${BASE_URL}/renders`, {
49 method: 'POST',
50 headers: {
51 'X-API-KEY': API_KEY,
52 'Content-Type': 'application/json',
53 },
54 body: JSON.stringify({
55 mockup_uuid: mockupId,
56 smart_objects: [{
57 uuid: smartObjectId,
58 asset: { url: designUrl, fit: 'cover' }
59 }],
60 export_options: {
61 image_format: 'webp',
62 image_size: 1920,
63 quality: 95
64 }
65 }),
66 })
67
68 const data = await response.json()
69 if (!data.success) throw new Error(data.detail)
70 return data.data.print_files[0].export_path
71}

React Hook for Managing Mockups

A custom hook that manages mockup state and provides useful computed values from the smart object metadata:

hooks/useMockupEditor.ts
typescript
1'use client'
2
3import { useState, useMemo } from 'react'
4import type { MockupData, SmartObject } from '@/lib/sudomock'
5
6export function useMockupEditor(initialMockup?: MockupData) {
7 const [mockup, setMockup] = useState<MockupData | null>(initialMockup ?? null)
8 const [selectedDesign, setSelectedDesign] = useState<string | null>(null)
9
10 // Find the main print area smart object
11 const printArea = useMemo(() => {
12 return mockup?.smart_objects.find(
13 so => so.name.toLowerCase().includes('print') ||
14 so.name.toLowerCase().includes('design')
15 )
16 }, [mockup])
17
18 // Calculate design requirements from size field
19 const designRequirements = useMemo(() => {
20 if (!printArea) return null
21
22 const { width, height } = printArea.size
23 return {
24 width,
25 height,
26 aspectRatio: width / height,
27 megapixels: (width * height) / 1000000,
28 recommendation: width >= 3000
29 ? 'High resolution - perfect for print'
30 : 'Medium resolution - good for web'
31 }
32 }, [printArea])
33
34 // Get render position info from position field
35 const renderPosition = useMemo(() => {
36 if (!printArea) return null
37
38 const { x, y, width, height } = printArea.position
39 const originalSize = printArea.size
40
41 return {
42 x,
43 y,
44 width,
45 height,
46 scaleFactor: width / originalSize.width,
47 // Useful for canvas preview
48 bounds: { left: x, top: y, right: x + width, bottom: y + height }
49 }
50 }, [printArea])
51
52 return {
53 mockup,
54 setMockup,
55 printArea,
56 designRequirements,
57 renderPosition,
58 selectedDesign,
59 setSelectedDesign,
60 }
61}

Live Preview with Canvas

Use the position data to create a real-time preview before rendering:

components/MockupPreview.tsx
typescript
1'use client'
2
3import { useRef, useEffect, useState } from 'react'
4
5interface PreviewProps {
6 mockupThumbnailUrl: string
7 designImageUrl: string | null
8 // From smart_object.position in API response
9 position: { x: number; y: number; width: number; height: number }
10}
11
12export function MockupPreview({ mockupThumbnailUrl, designImageUrl, position }: PreviewProps) {
13 const canvasRef = useRef<HTMLCanvasElement>(null)
14 const [previewDataUrl, setPreviewDataUrl] = useState<string | null>(null)
15
16 useEffect(() => {
17 if (!designImageUrl || !canvasRef.current) return
18
19 const canvas = canvasRef.current
20 const ctx = canvas.getContext('2d')
21 if (!ctx) return
22
23 // Load both images
24 const mockupImg = new Image()
25 const designImg = new Image()
26 mockupImg.crossOrigin = 'anonymous'
27 designImg.crossOrigin = 'anonymous'
28
29 Promise.all([
30 new Promise<void>(resolve => { mockupImg.onload = () => resolve(); mockupImg.src = mockupThumbnailUrl }),
31 new Promise<void>(resolve => { designImg.onload = () => resolve(); designImg.src = designImageUrl }),
32 ]).then(() => {
33 // Set canvas to mockup size
34 canvas.width = mockupImg.width
35 canvas.height = mockupImg.height
36
37 // Draw mockup background
38 ctx.drawImage(mockupImg, 0, 0)
39
40 // Draw design at the exact position from API
41 // The position object tells us exactly where to place it!
42 ctx.drawImage(
43 designImg,
44 position.x, // x coordinate from API
45 position.y, // y coordinate from API
46 position.width, // rendered width from API
47 position.height // rendered height from API
48 )
49
50 setPreviewDataUrl(canvas.toDataURL('image/png'))
51 })
52 }, [mockupThumbnailUrl, designImageUrl, position])
53
54 return (
55 <div className="relative aspect-square bg-zinc-900 rounded-lg overflow-hidden">
56 <canvas ref={canvasRef} className="hidden" />
57 {previewDataUrl && (
58 <img src={previewDataUrl} alt="Preview" className="w-full h-full object-contain" />
59 )}
60 </div>
61 )
62}

Server Action for Batch Rendering

Process multiple designs in parallel using Next.js Server Actions:

actions/batch-render.ts
typescript
1'use server'
2
3import { renderMockup } from '@/lib/sudomock'
4
5interface BatchResult {
6 designUrl: string
7 renderUrl: string | null
8 success: boolean
9 error?: string
10}
11
12export async function batchRenderMockups(
13 mockupId: string,
14 smartObjectId: string,
15 designUrls: string[]
16): Promise<BatchResult[]> {
17 // Process 5 at a time to respect rate limits
18 const CONCURRENCY = 5
19 const results: BatchResult[] = []
20
21 for (let i = 0; i < designUrls.length; i += CONCURRENCY) {
22 const batch = designUrls.slice(i, i + CONCURRENCY)
23
24 const batchResults = await Promise.all(
25 batch.map(async (designUrl): Promise<BatchResult> => {
26 try {
27 const renderUrl = await renderMockup(mockupId, smartObjectId, designUrl)
28 return { designUrl, renderUrl, success: true }
29 } catch (error) {
30 return {
31 designUrl,
32 renderUrl: null,
33 success: false,
34 error: error instanceof Error ? error.message : 'Unknown error'
35 }
36 }
37 })
38 )
39
40 results.push(...batchResults)
41 }
42
43 return results
44}

Environment Variables

Store your SudoMock API key in .env.local as SUDOMOCK_API_KEY. Never expose it in client-side code — always call SudoMock from Server Components, Server Actions, or API routes.

Key Takeaways

size is your design canvasAlways prepare designs at the size dimensions for best quality. The API handles scaling.
position is the rendered resultUse position data when building previews or custom renderers. It tells you exactly where designs appear.
quad enables advanced transformsFor perspective mockups or canvas-based rendering, quad coordinates give you precise control.
blend_mode matters for colorsUnderstanding blend modes helps you work with color-changing smart objects correctly.

Related Resources

Ready to Try SudoMock?

Start automating your mockups with 500 free API credits.