SudoMock
POST

Render (PSD)

Render an uploaded PSD by replacing Smart Object artwork, personalizing text layers, updating group outlines, or combining them in one request.

POST/api/v1/renders
<1s avg

Production Ready

Built for high-volume automation with async jobs, webhooks, and CDN-hosted output URLs.

Prefer no backend? Embed Studio

This endpoint renders from your server. If you would rather let your customers customize a PSD mockup on your storefront, embed the Studio editor with one iframe, no render pipeline to build.

base64 is Recommended (50-500ms faster)

Sending your image as base64 eliminates server-side download latency entirely. The server decodes the image instantly instead of fetching it over HTTP. For high-volume automation, this can save 50-500ms per render compared to URL-based delivery. Image source priority: base64 > url.

Request

Headers

x-api-keystring

Your SudoMock API key starting with sm_. Required unless using Bearer token authentication.

Authorizationstring

Alternative to x-api-key. Use Bearer <JWT> with a JWT from your dashboard login for dashboard/frontend requests. Format: Bearer eyJhbGciOi...

Content-TypestringRequired

Must be application/json

Authentication

Provide either x-api-key or Authorization: Bearer <JWT>. API keys are recommended for server-to-server integrations. Bearer tokens are used by the dashboard and frontend applications.

Request Body

mockup_uuidstringRequired

UUID of the mockup to render. Must be a valid UUID format (e.g., c315f78f-d2c7-4541-b240-a9372842de94). Obtained from the POST /psd/upload or GET /mockups response.

smart_objectsarray

Smart Object configurations to render. Each entry must have an asset, a color, or both. A request needs at least one entry across smart_objects, text_layers, or group_layers.

text_layersarray

Up to 50 text-layer overrides. Each entry targets a text-layer uuid and provides exactly one of text for a single-style layer or segments for a mixed-style layer. Omitted layers keep their authored values.

group_layersarray

Up to 50 group-outline overrides. Each entry targets a uuid from the upload or mockup-detail response group_layers list. See Group Outline Overrides below.

export_optionsobject

Output format, size, quality, and print DPI settings. All fields have defaults, so this entire object is optional.

export_labelstring

Optional label for the export file naming. Max 100 characters. Used as a prefix in the output filename.

At least one render input is required

A render must include at least one entry across smart_objects, text_layers, or group_layers. The REST endpoint can personalize a text-only PSD without a Smart Object.

Smart Object Configuration

Asset or Color Required

Each smart object must have at least an asset (image), a color overlay, or both. Omitting both will return a 422 validation error.
uuidstringRequired

UUID of the smart object to fill (from upload response). Must be a valid UUID format.

assetobject

Image asset configuration. Either asset or color (or both) must be provided.

asset.base64string

Raw base64-encoded image bytes (no data: prefix). RECOMMENDED: eliminates server-side download latency (50-500ms faster than URL). Either base64 or url must be provided.

asset.urlstring

URL to the image (HTTP/HTTPS or data: URL). Server downloads the image, adding network latency. Either url or base64 must be provided.

asset.content_typestring

MIME type when using the base64 field. Must match pattern: image/png, image/jpeg, image/webp, or image/gif. Defaults to image/png if omitted.

asset.fitenum= fit

How the artwork meets the smart object area. fit = scale it until it fits inside, proportions kept, which can leave empty space. fill = stretch it to the bounds, proportions not kept, which distorts artwork whose aspect ratio differs from the area. crop = cover the area and cut the overflow, proportions kept. contain and cover are the older names for fit and crop and are still accepted, so calls written against them keep working. An unrecognised value returns 422 rather than falling back to a default.

asset.rotateinteger= 0

Rotation angle in degrees (-360 to 360)

asset.flip_horizontalboolean= false

Flip artwork horizontally (left-right mirror)

asset.flip_verticalboolean= false

Flip artwork vertically (top-bottom mirror)

asset.remove_backgroundboolean= false

Remove the image background before placing the artwork. Pricing starts at about $0.06 per unique artwork source.

asset.sizeobject

Custom size override. Both width and height are optional (min: 1 pixel each).

asset.size.widthinteger

Custom width in pixels (min: 1)

asset.size.heightinteger

Custom height in pixels (min: 1)

asset.positionobject

Custom position override in pixels.

asset.position.topinteger

Top offset in the smart object's own embedded pixel frame, measured from that frame's top-left corner. Omit it to centre the artwork vertically.

asset.position.leftinteger

Left offset in the smart object's own embedded pixel frame, measured from that frame's top-left corner. Omit it to centre the artwork horizontally.

colorobject

Color overlay configuration. Either asset or color (or both) must be provided.

color.hexstringRequired

Hex color code in #RRGGBB format (e.g., '#FF5733'). Must match pattern: #[0-9A-Fa-f]{6}.

color.blending_modestring= normal

Blend mode for the color overlay. All 27 Photoshop blend modes supported. See Blend Modes section below for the full list.

adjustment_layersobject

Image adjustments applied to the user's artwork before blending into the mockup. Applied after fit transformation. Does not affect PSD-level adjustments.

Adjustment Layers

brightnessinteger= 0

Brightness adjustment (-150 to 150). 0 = no change.

contrastinteger= 0

Contrast adjustment (-100 to 100). 0 = no change.

saturationinteger= 0

Saturation adjustment (-100 to 100). 0 = no change, -100 = grayscale.

vibranceinteger= 0

Vibrance adjustment (-100 to 100). Similar to saturation but preserves skin tones.

opacityinteger= 100

Artwork opacity (0 to 100). 0 = fully transparent, 100 = fully opaque.

blurinteger= 0

Gaussian blur amount (0 to 100). 0 = sharp, 100 = max blur. Useful for background effects.

Text Layer Overrides

Personalize up to 50 live text layers in a PSD. Each override provides exactly one of text or segments. Send only the layers you want to change; omitted layers keep their authored values. Get each layer's uuid from the upload or get-mockup response. The full walkthrough, including the font catalog and custom fonts, is on the Text Layers page.

uuidstringRequired

UUID of the text layer to change (from the upload or get-mockup response).

textstring

Replacement text for a single-style layer, 1 to 500 characters. Mutually exclusive with segments.

segmentsarray

For a mixed-style layer, 1 to 32 indexed segment overrides. Mutually exclusive with text. Omitted segments keep their original text. A request can contain at most 200 segment overrides across all text layers, and the effective combined text for one layer can be at most 500 characters.

segments[].indexinteger

Zero-based segment index from the upload or mockup-detail response, 0 to 31.

segments[].textstring

Replacement text for this segment, 1 to 200 characters. The segment keeps its own styling.

fontstring

For a single-style layer, a catalog font uuid or PostScript name from GET /api/v1/fonts. Omit it to use the PSD layer's original font. If an original or resolved catalog font file cannot be loaded, the render succeeds with a TEXT_FONT_FALLBACK warning. An explicitly requested font that is not in your catalog returns 422 FONT_NOT_FOUND.

font_sizeinteger

Font size in pixels for a single-style layer. Defaults to the layer's original size.

colorstring

Text color in #RRGGBB format for a single-style layer. Changes the color you see, including on layers whose visible color comes from a color effect. Defaults to the layer's original color.

stroke_colorstring | array

For a single-style layer, recolors only this text layer's own outlines. A #RRGGBB string changes the front outline; a 1 to 8 entry front-to-back list changes a stack, with null preserving an authored slot. Extra entries beyond stroke_count are ignored. A layer with no outline of its own ignores the value and returns a TEXT_STROKE_NOT_PRESENT warning. Use group_layers for an enclosing group outline.

fitenum= overflow

For single-style point text, controls replacement text wider than the layer's area: shrink scales it down to fit, clip keeps the size and cuts it, overflow keeps the size and lets it extend past. Paragraph text wraps within its box.

vertical_alignenum= top

Where shrunk text sits vertically inside the layer's original area: top keeps the designed position, center places it in the vertical middle, bottom places it on the bottom edge. Applies only when shrink actually reduces the text on a single-style point-text layer.

Point, multi-line, area, rotated, and warped text are editable

Single-line, multi-line, and area (paragraph) text are all editable, including mixed-style layers you edit segment by segment. Rotated text and ten warp styles (Arc, Arc Lower, Arc Upper, Arch, Bulge, Flag, Wave, Fish, Rise, and Squeeze) render live with your new text; the remaining warp styles and vertical text render with their original appearance in this version. Targeting a hidden text layer renders it, which makes personalization slots easy to build.
Text-only render with a text override
1{
2 "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
3 "text_layers": [
4 {
5 "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
6 "text": "SUMMER SALE",
7 "font": "Poppins-Bold",
8 "font_size": 96,
9 "color": "#C0392B",
10 "stroke_color": ["#C0392B", "#FFFFFF"]
11 }
12 ]
13}

Group Outline Overrides

Use group_layers to recolor outlines owned by an enclosing group. You can send up to 50 overrides. Target only groups listed in the upload or mockup-detail response group_layers array; changing one group outline affects everything inside that group.

group_layers[].uuidstringRequired

Lowercase uuid from the upload or mockup-detail response group_layers list.

group_layers[].stroke_colorstring | arrayRequired

A #RRGGBB string changes the group's front outline. For stacked outlines, send a 1 to 8 entry front-to-back list; null keeps an authored slot, and entries beyond the group's stroke_count are ignored.

Export Options

image_formatenum= webp

Output format: webp (30-70% smaller, recommended), png (lossless, quality setting ignored), or jpg (smallest, no transparency).

image_sizeinteger= 2048

Output width in pixels (100-10000). Height is auto-calculated from the PSD aspect ratio. This sets the actual pixel dimensions of the file. An account in trial is capped at 1024: a larger value is rejected with 402 OUTPUT_RESOLUTION_LIMIT rather than quietly downscaled, so you are never billed for a size you did not ask for.

qualityinteger= 90

Compression quality for JPG and WebP (1-100). Ignored for PNG, which is always lossless.

dpiinteger= null

Print resolution tag written into the output file metadata (72-2400). This is a metadata tag only; it does not change the pixels. image_size controls the actual pixels. For a true print file, size your pixels: image_size = print_inches x dpi (e.g. 12 in x 300 = 3600 px). All three formats (JPG, PNG, WebP) carry the tag; JPG and PNG are recommended for maximum print-tool compatibility because their resolution lives in the universally-read density field. Opt-in: when omitted, files carry a default 25.4 DPI tag.

WebP Recommended

WebP format is 30-70% smaller than PNG with similar visual quality. Perfect for web and e-commerce. PNG quality setting is ignored since PNG is always lossless.

Trial accounts are capped at 1,024 px

Until a card is verified and the balance is funded, image_size above 1024 returns 402 OUTPUT_RESOLUTION_LIMIT, and renders carry a watermark. The examples on this page use larger sizes because that is what you will ship. To run them as written, either lower image_size to 1024 while you build, or verify a card and fund the balance. Nothing else about the request or the response changes, so the same code works before and after.

Print-Ready Files (300 DPI)

For a print-ready file, set export_options.dpi and size your pixels for the physical dimensions. The dpi field only stamps a resolution tag into the metadata; image_size sets the real pixels. Use the formula image_size = print_inches x dpi. For a 12-inch print at 300 DPI, send image_size: 3600 and dpi: 300. Choose jpg or png for the widest print-tool compatibility.
Print-Ready Export Example
1{
2 "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
3 "smart_objects": [
4 {
5 "uuid": "128394ee-6758-4f2f-aa36-e2b19b152bd9",
6 "asset": {
7 "url": "https://your-domain.com/design.png",
8 "fit": "crop"
9 }
10 }
11 ],
12 "export_options": {
13 "image_format": "jpg",
14 "image_size": 3600,
15 "dpi": 300
16 }
17}
Full Request Example
1{
2 "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
3 "smart_objects": [
4 {
5 "uuid": "128394ee-6758-4f2f-aa36-e2b19b152bd9",
6 "asset": {
7 "base64": "iVBORw0KGgoAAAANSUhEUgAA...",
8 "content_type": "image/png",
9 "fit": "crop"
10 },
11 "color": {
12 "hex": "#FF5733",
13 "blending_mode": "multiply"
14 },
15 "adjustment_layers": {
16 "brightness": 10,
17 "contrast": 5
18 }
19 }
20 ],
21 "export_options": {
22 "image_format": "webp",
23 "image_size": 1920,
24 "quality": 90
25 }
26}

Code Examples

Render Mockup
bash
1curl -X POST "https://api.sudomock.com/api/v1/renders" \
2 -H "Content-Type: application/json" \
3 -H "x-api-key: sm_your_api_key" \
4 -d '{
5 "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
6 "smart_objects": [{
7 "uuid": "128394ee-6758-4f2f-aa36-e2b19b152bd9",
8 "asset": {
9 "url": "https://your-domain.com/design.png",
10 "fit": "crop"
11 }
12 }],
13 "export_options": {
14 "image_format": "webp",
15 "image_size": 1920,
16 "quality": 90
17 }
18 }'

Response

Success Response

200OK
Response 200 OK
1{
2 "success": true,
3 "data": {
4 "print_files": [
5 {
6 "export_path": "https://cdn.sudomock.com/renders/c315f78f-.../render_1711234567890.webp",
7 "smart_object_uuid": "128394ee-6758-4f2f-aa36-e2b19b152bd9",
8 "render_uuid": "9adcc828-1f4e-4a2b-9c3d-2e6f8b0a1d77"
9 }
10 ],
11 "render_uuid": "9adcc828-1f4e-4a2b-9c3d-2e6f8b0a1d77"
12 }
13}

Response Fields

successbooleanRequired

Always true for successful renders.

dataobjectRequired

Response data wrapper containing the rendered output.

data.print_filesarrayRequired

Array of rendered output files.

data.print_files[].export_pathstringRequired

CDN URL to the rendered mockup image. For standard API users, files are subject to a 7-day retention policy. For custom domain users, files are not on the 7-day schedule for as long as the custom domain stays connected, though some render types may still be removed sooner.

data.print_files[].smart_object_uuidstringRequired

UUID of the first Smart Object in the request. This is an empty string for a render without Smart Objects, including text-only and group-only renders.

data.print_files[].render_uuidstring

UUID identifying this render. Also returned at data.render_uuid. Use it to correlate the render with jobs and webhook deliveries.

data.render_uuidstring

UUID identifying this render (same value as data.print_files[].render_uuid).

warningsarray

Non-fatal advisories on a successful render. Omitted when there are none. Each item contains only code and message.

warnings[].codestring

Stable warning code for programmatic branching.

warnings[].messagestring

Human-readable description of the non-fatal result.

Error Responses

400Bad Request

Invalid request parameters, including a Smart Object, text layer, or group layer uuid that does not belong to this mockup.

json
1{
2 "detail": "Smart object 128394ee-... not found in mockup",
3 "success": false
4}
401Unauthorized

Missing or invalid API key / Bearer token. Possible messages: "Not authenticated", "Invalid API key format. Must start with 'sm_'", "Invalid or revoked API key".

json
1{
2 "detail": "Not authenticated",
3 "success": false
4}
402Payment Required

The request cannot be paid for, or the account is in trial and the request is above a trial limit. Billing refusals carry error and actions; trial limit refusals carry error_code and details. See Error Handling for every case.

json
1{
2 "error": "credits_exhausted",
3 "message": "You have used all 500 trial credits. Add a payment method to keep rendering, or start a plan.",
4 "actions": [
5 { "label": "Add a payment method", "url": "https://sudomock.com/dashboard/billing?action=topup#payg-section" },
6 { "label": "Start a plan", "url": "https://sudomock.com/pricing" }
7 ]
8}
json
1{
2 "error_code": "OUTPUT_RESOLUTION_LIMIT",
3 "message": "Your account is in trial, so output is capped at 1024px.",
4 "details": {
5 "requested_width": 4096,
6 "limit": 1024,
7 "suggestion": "Lower image_size to 1024 or below, or add a payment method to render at full width."
8 },
9 "success": false
10}
403Forbidden

The mockup is tied to an active Studio session for a different mockup. Finish or exit that Studio session, then retry the render against this mockup.

json
1{
2 "detail": "This mockup is locked to an active Studio session for a different mockup.",
3 "success": false
4}
404Not Found

Mockup UUID does not exist.

json
1{
2 "detail": "Mockup not found: c315f78f-d2c7-4541-b240-a9372842de94",
3 "success": false
4}
422Validation Error

Request validation fails, for example because all three render-input arrays are empty, a text override does not provide exactly one of text or segments, an explicit font is unknown, or a group outline is not editable.

json
1{
2 "detail": "Validation error",
3 "errors": [
4 {
5 "field": "body -> smart_objects -> 0",
6 "message": "Invalid value for field: body -> smart_objects -> 0"
7 }
8 ],
9 "success": false
10}
429Too Many Requests

Rate limit or concurrent render limit exceeded. Includes structured retry information.

json
1{
2 "detail": "Rate limit exceeded. Try again in 30 seconds.",
3 "error": {
4 "type": "rate_limit_exceeded",
5 "code": "RATE_LIMIT_EXCEEDED",
6 "limit": 1000,
7 "remaining": 0,
8 "reset_seconds": 30,
9 "retry_after": 30,
10 "resource": "api"
11 }
12}
500Internal Server Error

Rendering failed unexpectedly. Error messages are user-friendly and suggest next steps.

json
1{
2 "detail": "Rendering failed unexpectedly. Please try again or contact support. (Error: INTERNAL_ERROR)",
3 "success": false
4}
502Bad Gateway

The upstream image source returned a 5xx error or is unreachable. This is transient and safe to retry with backoff. User-side image URL errors (404 / 403 / 400, such as a bad or expired URL) now return 400 instead, which is permanent: fix the URL rather than retrying.

json
1{
2 "detail": "The image source for 'Front Design' is temporarily unavailable. cdn.example.com returned HTTP 503. Please try again later.",
3 "success": false
4}

Image Fit Modes

The fit parameter controls how your design is placed within the smart object bounds:

ModeBehaviorUse Case
fillStretches to fill entire area (may distort if aspect ratios differ)When aspect ratio matches or distortion is acceptable
fitFits inside preserving aspect ratio (may leave empty space)When the full design must be visible
cropCovers the area and crops the overflow, proportions kept (no distortion, no empty space)Most common for product mockups

Why fit is the default

A call that says nothing about fit is not asking to have its artwork distorted, so the default is the mode that never distorts. Sendfill explicitly when the artwork already matches the area's aspect ratio, or when stretching is what you want.

The same reasoning decides what happens to a value we do not recognise: it returns 422 instead of quietly resolving to a default. A typo that renders a wrong image without an error is far more expensive than a rejected request, because nobody sees it until a customer does.

Working with Smart Object Bounds

When rendering, it helps to understand how the smart object metadata from the upload response relates to your design:

Typical Integration Workflow

1
Get smart object metadata from upload
The size field tells you the design canvas dimensions (e.g., 3000x3413)
2
Prepare your design at the size dimensions
For best quality, create designs at the size.width x size.height resolution
3
Render with the smart object UUID
The API handles scaling to the position bounds automatically

Design Resolution Tip

Always design at the size dimensions from the upload response. The design will be automatically scaled to fit the position bounds on the mockup canvas. This ensures maximum quality.

Custom Position Overrides

You can override the default positioning using asset.size and asset.position:

Custom Position Override
1{
2 "smart_objects": [{
3 "uuid": "smart-object-uuid",
4 "asset": {
5 "url": "https://example.com/design.png",
6 "fit": "crop",
7 "size": { "width": 2000, "height": 2000 },
8 "position": { "top": 100, "left": 50 }
9 }
10 }]
11}

Use position overrides when you need precise control over design placement, such as centering a logo or offsetting a pattern.

Blend Modes

Apply color overlays with any of the 27 supported blending modes. All standard Photoshop layer blend modes are supported. Use underscore or space separators (e.g., soft_light or soft light). Pass Through is additionally accepted for group layers (group blending mode, not counted in the 27).

Normal

normal
dissolve

Darken

darken
multiply
color burn
linear burn
darker color

Lighten

lighten
screen
color dodge
linear dodge (add)
lighter color

Contrast

overlay
soft light
hard light
vivid light
linear light
pin light
hard mix

Inversion

difference
exclusion
subtract
divide

Component (HSL)

hue
saturation
color
luminosity

Bulk Rendering

Parallel Renders

For bulk rendering, send parallel requests up to your plan's parallel render limit. Each request is independent-no need to wait for previous renders to complete.

Prefer async for large batches

For big batches or long-running work, add "is_async": true to get an immediate 202 with a job_id instead of holding the connection open. Then track each job by polling or via a webhook. See Async Rendering and Jobs.

Need a feature that's not here? Request it or see what's planned.

Bulk Rendering Example
1// JavaScript: Render 10 variations in parallel
2const designs = [
3 "https://cdn.example.com/design-1.png",
4 "https://cdn.example.com/design-2.png",
5 // ... more designs
6];
7
8const renderPromises = designs.map(designUrl =>
9 fetch("https://api.sudomock.com/api/v1/renders", {
10 method: "POST",
11 headers: {
12 "Content-Type": "application/json",
13 "x-api-key": "sm_your_api_key"
14 },
15 body: JSON.stringify({
16 mockup_uuid: "your-mockup-uuid",
17 smart_objects: [{
18 uuid: "smart-object-uuid",
19 asset: { url: designUrl, fit: "crop" }
20 }]
21 })
22 }).then(r => r.json())
23);
24
25const results = await Promise.all(renderPromises);
26const renderUrls = results.map(r => r.data.print_files[0].export_path);

Try It Live

POST/api/v1/renders

Render a mockup with your design.

Get your API key from the Dashboard