SudoMock
REST

2D Mockups (Setup)

Place your artwork onto a product photo. Turn any apparel, poster, or packshot image into a reusable mockup, then render unlimited designs onto its print areas.

When to use this

Use the 2D Mockup API when you start from a flat product photo (a t-shirt, hoodie, poster, mug, or packaging shot) rather than a layered PSD template. If you already have PSD templates, use the Render (PSD) endpoint instead.

2D is a two-step flow

Step 1 is this page: set up a reusable mockup from a product photo. Step 2 is Render (2D): render any design onto that mockup. You set up once, then render as many artworks as you like.

How It Works

A 2D mockup is created once and reused for every design. Creating it is synchronous by default: the printable areas on your product photo are found and prepared, and the call returns the ready mockup in one call, no polling. Pass is_async: true for a queued job with webhook delivery instead. Rendering is also synchronous by default and accepts is_async: true when you want a queued 2d_render job.

1
Create a mockup from a product photo
POST /api/v1/sudoai/2d-mockups returns the ready mockup with its print-area quads. Costs 25 credits.
2
Read the print-area quads
The create response carries the quads. With is_async: true, poll the job or receive a webhook first, then fetch the mockup.
3
Render artwork onto the print areas
POST /api/v1/sudoai/2d-mockups/{mockup_uuid}/render returns a hosted image. Costs 5 credits.

Create once, render forever

A mockup is reusable. You pay 25 credits to create it, then 5 credits per render for as many designs as you like. There is no watermark on 2D renders.

What Teams Build With It

One product photo, prepared once, covers very different jobs. These are the three shapes this flow usually takes.

Authentication

Every request uses your API key in the x-api-key header. Get a key from your Dashboard.

x-api-keystringRequired

Your SudoMock API key, starting with sm_.

Content-TypestringRequired

Must be application/json for POST and PUT requests.

Idempotency-Keystring

Optional. Safely retry a create request without charging twice. Max 255 characters. Reusing a key with a different body returns 409.

Create a Mockup

POST/api/v1/sudoai/2d-mockups
25 credits

Send exactly one image source. By default the create is synchronous: the call blocks until the mockup is ready and returns it with a 201 Created in one call, no polling. Pass is_async: true to queue it instead and get a 202 Accepted job you can poll (or receive a webhook). Either way, if the image cannot be used for a mockup, the 25 credits are refunded automatically.

Request Body

source_urlstring

Public HTTPS URL of the product photo. Provide either source_url or source_base64, not both.

source_base64string

Base64-encoded product photo, with or without a data URL prefix. Provide either source_base64 or source_url, not both.

namestring

Optional display name for the mockup. Max 255 characters.

is_asyncboolean

Optional, default false. When false the call returns the finished mockup (201). When true it returns immediately with a 202 job to poll (or a webhook), useful for batch creates.

print_areasarray

Optional list of one to eight print areas. When supplied, SudoMock uses these areas exactly and skips automatic print-area detection.

print_areas[].namestring

Optional label for the print area, such as Front or Back. Max 120 characters.

print_areas[].pointsarray

Exactly four [x, y] points in source-image pixels, ordered top-left, top-right, bottom-right, bottom-left.

Create request
1{
2 "source_url": "https://your-domain.com/product-photo.jpg",
3 "name": "Classic Tee - Front"
4}

Already know the print areas?

Include print_areas in the create request to use your own one-to-eight four-point areas and skip automatic detection.
Create with known print areas
1{
2 "source_url": "https://your-domain.com/product-photo.jpg",
3 "name": "Classic Tee - Front",
4 "print_areas": [
5 {
6 "name": "Front",
7 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]]
8 }
9 ]
10}

Response (default, synchronous)

201Created
Response 201 Created
1{
2 "data": {
3 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
4 "name": "Classic Tee - Front",
5 "status": "ready",
6 "thumbnail_url": "https://cdn.sudomock.com/2d-mockups/9b0a74cd.../thumb.webp",
7 "source_width": 2000,
8 "source_height": 2400,
9 "quads": [
10 {
11 "print_area_id": "223e4567-e89b-12d3-a456-426614174001",
12 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]],
13 "sort_order": 0
14 }
15 ],
16 "surfaces": [],
17 "version": 1,
18 "created_at": "2026-07-20T10:00:00Z",
19 "updated_at": "2026-07-20T10:00:12Z"
20 },
21 "success": true
22}

The synchronous response is the mockup itself, including its print-area quads, so you can go straight to rendering. See Track and Fetch the Mockup below for the is_async: true job flow.

Track and Fetch the Mockup

GET/api/v1/sudoai/2d-mockups/{mockup_uuid}
Free

You only need this when you pass is_async: true. The synchronous default already returns the mockup. For an async create, poll the job at status_url until its status becomes succeeded (or receive a webhook); the completed job carries the new mockup_uuid. See the Jobs reference for the full poll contract.

Create response with is_async: true, 202 Accepted
1{
2 "job_id": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
3 "kind": "2d_create",
4 "status": "queued",
5 "status_url": "/api/v1/jobs/b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60"
6}
GET /api/v1/jobs/{job_id} once ready
1{
2 "job_id": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
3 "kind": "2d_create",
4 "status": "succeeded",
5 "result_url": "/api/v1/sudoai/2d-mockups/9b0a74cd-0ebb-4748-a748-976fec8cc000",
6 "mockup_uuid": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
7 "error": null,
8 "created_at": "2026-07-20T10:00:00Z",
9 "updated_at": "2026-07-20T10:00:12Z"
10}

Then fetch the mockup itself to read its render targets. Standard print areas are four-corner quads in image coordinates, returned in the quads array. Verified full product surfaces are returned separately in surfaces.

GET /api/v1/sudoai/2d-mockups/{mockup_uuid}
1{
2 "data": {
3 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
4 "name": "Classic Tee - Front",
5 "status": "ready",
6 "customizable": true,
7 "thumbnail_url": "https://cdn.sudomock.com/2d-mockups/9b0a74cd.../thumb.webp",
8 "source_width": 2000,
9 "source_height": 2400,
10 "quads": [
11 {
12 "print_area_id": "223e4567-e89b-12d3-a456-426614174001",
13 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]],
14 "sort_order": 0
15 }
16 ],
17 "surfaces": [],
18 "version": 1,
19 "created_at": "2026-07-20T10:00:00Z",
20 "updated_at": "2026-07-20T10:00:12Z"
21 },
22 "success": true
23}

Mockup Fields

data.mockup_idstringRequired

UUID of the mockup. Pass it in the render URL path.

data.namestringRequired

Display name of the mockup.

data.statusstringRequired

Mockup state: draft, processing, ready, or error. Print areas are usable once ready.

data.customizablebooleanRequired

True when this mockup can open a shopper customization session.

data.thumbnail_urlstring

Preview thumbnail URL.

data.source_widthinteger

Source photo width in pixels. Print-area points are in this coordinate space.

data.source_heightinteger

Source photo height in pixels.

data.quadsarrayRequired

Print areas detected on the photo. Each entry has print_area_id, points, and sort_order.

data.quads[].print_area_idstringRequired

UUID of the print area. Pass this as uuid inside the render print_areas array.

data.quads[].pointsarrayRequired

Four [x, y] corner points, ordered top-left, top-right, bottom-right, bottom-left.

data.quads[].sort_orderintegerRequired

Display order of the print area, starting at 0.

data.surfacesarrayRequired

Verified full product surfaces. Empty for a standard print-area mockup.

data.surfaces[].surface_uuidstring

UUID of a full product surface. Pass this as surface_uuid inside the render print_areas array.

List Mockups

GET/api/v1/sudoai/2d-mockups
Free

Returns your 2D mockups newest first. Each item includes its status, thumbnail, source dimensions, and saved print areas.

limitinteger= 20

Items per page, from 1 to 100.

offsetinteger= 0

Number of items to skip.

customizable_onlyboolean= false

When true, return only mockups ready for shopper customization.

Response 200 OK
1{
2 "data": [
3 {
4 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
5 "name": "Classic Tee - Front",
6 "status": "ready",
7 "customizable": true,
8 "thumbnail_url": "https://cdn.sudomock.com/2d-mockups/9b0a74cd.../thumb.webp",
9 "source_width": 2000,
10 "source_height": 2400,
11 "print_areas": [
12 {
13 "print_area_id": "223e4567-e89b-12d3-a456-426614174001",
14 "name": "Front",
15 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]],
16 "sort_order": 0
17 }
18 ],
19 "version": 1,
20 "created_at": "2026-07-20T10:00:00Z",
21 "updated_at": "2026-07-20T10:00:12Z"
22 }
23 ],
24 "total": 1,
25 "limit": 20,
26 "offset": 0,
27 "success": true
28}
PUT/api/v1/sudoai/2d-mockups/{mockup_uuid}/print-areas
Free

The print areas prepared at creation work out of the box. Use this endpoint only when you want to override them with your own placement. It replaces all print areas in the order you send, so include every area you want to keep. Available once the mockup is ready.

Want to review or adjust the prepared result visually? A Studio setup session opens the full toolbar where you can refine supported product areas, adjust bounded print areas, and preview placement before saving. See Setting up print areas.

print_areasarrayRequired

Zero to eight print areas. Array order becomes the sort order. Send an empty array to remove every print area.

print_areas[].pointsarrayRequired

Exactly four [x, y] corner points, in image coordinates, ordered top-left, top-right, bottom-right, bottom-left. Each area must be a convex four-point shape inside the source image.

Replace print areas
1{
2 "print_areas": [
3 {
4 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]]
5 }
6 ]
7}
200OK
Response 200 OK
1{
2 "data": {
3 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
4 "print_areas": [
5 {
6 "print_area_id": "223e4567-e89b-12d3-a456-426614174001",
7 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]],
8 "sort_order": 0
9 }
10 ]
11 },
12 "success": true
13}

Render Artwork

POST/api/v1/sudoai/2d-mockups/{mockup_uuid}/render
5 credits

Render one or more designs onto a mockup you created earlier. Each print area takes an artwork image, a solid color, or both. By default the response returns a hosted image URL synchronously. Set is_async: true to receive a queued job instead.

Choose exactly one target identifier

Use uuid for a saved entry from data.quads. Use surface_uuid for a verified full product surface from data.surfaces. Do not send both.

Request Body

mockup_uuidstringRequired

Path parameter in the URL, not the body: UUID of the 2D mockup to render, from the create job or the get-mockup response.

print_areasarrayRequired

One to eight render targets. Each uses exactly one uuid or surface_uuid from the get-mockup response.

export_optionsobject

Output format, size, and quality. Optional; sensible defaults apply.

is_asyncboolean

Optional, default false. When true, returns a 202 2d_render job to poll or receive by webhook.

Artwork or color required

Each print area must include at least an artwork_url (or base64) or a color. You can supply both to tint the artwork.
uuidstring

The print_area_id from data.quads. Required when surface_uuid is omitted.

surface_uuidstring

The identifier from data.surfaces. Required when uuid is omitted.

artwork_urlstring

URL of the artwork image to place. Provide artwork_url or base64.

base64string

Base64-encoded artwork image. Skips the download step. Provide base64 or artwork_url.

colorstring

Solid color overlay in #RRGGBB format (e.g. '#FF0000'). Can be used alone or to tint the artwork.

adjustmentsobject

Per-artwork image adjustments. See Adjustments below.

placementobject

How the artwork sits inside the print area. See Placement below.

Adjustments

brightnessinteger= 0

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

contrastinteger= 0

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

saturationinteger= 0

Saturation (-100 to 100). -100 = grayscale.

vibranceinteger= 0

Vibrance (-100 to 100). Boosts muted tones while protecting skin tones.

opacityinteger= 100

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

blurinteger= 0

Gaussian blur (0 to 100). 0 = sharp.

blend_modeenum= multiply

How the artwork sits on the product surface. 'multiply' keeps the fabric or material texture visible; 'normal' lays the artwork flat and reproduces the artwork colors exactly; 'screen' keeps a light artwork readable on a dark garment; 'lighten' keeps the artwork only where it is brighter than the surface; 'soft_light' gives a subtle, low-contrast finish; 'overlay' deepens contrast so the artwork reads as part of the material; 'darken' keeps the artwork only where it is darker than the surface. Send 'normal' when a brand color has to match the supplied file.

Keeping a brand color exact

multiply is the default because it looks like a real print: on a black tee a white logo comes out grey, the way ink looks on dark fabric. When a brand color has to match the file you supplied, send "blend_mode": "normal": white stays white and a brand blue stays that blue, whatever the product color. Blend mode is set per print area, so one area can hold an exact logo while another stays on multiply. The other five modes are for a specific look: screen on a dark garment, overlay for stronger contrast, soft_light for a gentle finish, darken and lighten to keep only the darker or lighter parts of the artwork.

Placement

positionenum= center

Anchor within the print area: center, top_left, top_center, top_right, center_left, center_right, bottom_left, bottom_center, bottom_right.

coverageinteger= 70

Percent of the print area the artwork covers (10 to 100). Ignored when width and height are set.

fitenum= contain

How the artwork fits its box: 'contain' fits inside preserving aspect ratio, 'fill' stretches to the bounds, 'cover' fills and crops excess.

widthinteger

Artwork width in print-area pixels (1 to 30000). Send it with height; the pair overrides coverage and fit sizing. The axes are independent, so any aspect ratio is allowed.

heightinteger

Artwork height in print-area pixels (1 to 30000). Send it with width; sending one alone is rejected rather than guessed.

rotationnumber= 0

Rotation in degrees, clockwise positive.

offset_xinteger= 0

Horizontal pixel offset from the anchor position.

offset_yinteger= 0

Vertical pixel offset from the anchor position.

Export Options

image_formatenum= webp

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

image_sizeinteger= 2048

Output width in pixels (100 to 10000). Height is derived from the source aspect ratio. Powers of two (1024, 2048, 4096) are recommended.

qualityinteger= 90

Compression quality for JPG and WebP (1 to 100). Ignored for PNG.

dpiinteger= null

Print resolution tag written into the file metadata (72 to 2400). Metadata only; image_size controls the real pixels. For a print file, size image_size = print_inches x dpi.

Standard print-area render
1{
2 "print_areas": [
3 {
4 "uuid": "223e4567-e89b-12d3-a456-426614174001",
5 "artwork_url": "https://your-domain.com/design.png",
6 "adjustments": {
7 "blend_mode": "multiply",
8 "opacity": 95
9 },
10 "placement": {
11 "position": "center",
12 "coverage": 80,
13 "fit": "contain"
14 }
15 }
16 ],
17 "export_options": {
18 "image_format": "png",
19 "image_size": 2048,
20 "quality": 95
21 }
22}
Full product-surface render
1{
2 "print_areas": [
3 {
4 "surface_uuid": "323e4567-e89b-12d3-a456-426614174002",
5 "artwork_url": "https://your-domain.com/design.png",
6 "placement": {
7 "position": "center",
8 "fit": "cover"
9 }
10 }
11 ],
12 "export_options": {
13 "image_format": "png",
14 "image_size": 2048
15 }
16}

Response

200OK
Response 200 OK
1{
2 "data": {
3 "render_uuid": "9adcc828-1f4e-4a2b-9c3d-2e6f8b0a1d77",
4 "print_files": [
5 {
6 "export_path": "https://cdn.sudomock.com/renders/2d/9b0a74cd.../render.png",
7 "duration_ms": 1180,
8 "export_format": "png"
9 }
10 ]
11 },
12 "success": true
13}
data.render_uuidstring

Identifier for this render.

data.print_filesarrayRequired

Rendered output files.

data.print_files[].export_pathstringRequired

Hosted URL of the rendered image.

data.print_files[].duration_msinteger

Render duration in milliseconds.

data.print_files[].export_formatstring

Output format used (png, jpg, or webp).

Asynchronous Response

202Accepted
Response 202 Accepted with is_async: true
1{
2 "job_id": "c315f78f-d2c7-4541-b240-a9372842de94",
3 "kind": "2d_render",
4 "status": "queued",
5 "status_url": "/api/v1/jobs/c315f78f-d2c7-4541-b240-a9372842de94"
6}

Poll status_url or subscribe to 2d_render.succeeded and 2d_render.failed. The completed job puts the rendered image URL in result_url.

No watermark

2D renders are delivered without a watermark. The output URL is ready to use in your store, catalog, or fulfillment flow.

Prefer no backend? Embed Studio

If you want your customers to place their own artwork on this mockup, embed the Studio editor with one iframe. Studio handles upload, placement, live preview, and add to cart, so you skip building a render frontend.

Security

Studio uses an opaque, origin-bound session token, and config writes are gated to your API key. Studio requests and responses expose no pipeline internals, only the mockup contract. See the Studio security model.

Delete a 2D Mockup

DELETE/api/v1/sudoai/2d-mockups/{mockup_uuid}
Free

Permanently deletes a 2D mockup and its print areas. This action cannot be undone.

200OK
Response 200 OK
1{
2 "data": {
3 "deleted": "9b0a74cd-0ebb-4748-a748-976fec8cc000"
4 },
5 "success": true
6}

Code Examples

Create and render a 2D mockup
bash
1# 1. Create a reusable 2D mockup (synchronous: the call returns the ready mockup)
2curl -X POST "https://api.sudomock.com/api/v1/sudoai/2d-mockups" \
3 -H "Content-Type: application/json" \
4 -H "x-api-key: sm_your_api_key" \
5 -d '{
6 "source_url": "https://your-domain.com/product-photo.jpg",
7 "name": "Classic Tee - Front"
8 }'
9# -> 201 { "data": { "mockup_id": "...", "quads": [{ "print_area_id": "...", ... }] } }
10# Add "is_async": true to get a 202 { "job_id", "status_url" } job to poll instead.
11
12# 2. Render your artwork onto a print area
13curl -X POST "https://api.sudomock.com/api/v1/sudoai/2d-mockups/MOCKUP_UUID/render" \
14 -H "Content-Type: application/json" \
15 -H "x-api-key: sm_your_api_key" \
16 -d '{
17 "print_areas": [{
18 "uuid": "PRINT_AREA_ID",
19 "artwork_url": "https://your-domain.com/design.png",
20 "placement": { "position": "center", "coverage": 80 }
21 }],
22 "export_options": { "image_format": "png", "image_size": 2048 }
23 }'

Webhooks

Instead of polling, configure a webhook endpoint to be notified when a creation job finishes. Set it up and verify signatures on the Webhooks page. The 2D creation and asynchronous render flows emit these events:

2d_mockup.readyevent

The mockup was created and its print areas are ready to render.

2d_mockup.rejectedevent

The image was not suitable for a mockup. The payload includes a reason. Credits are refunded.

2d_mockup.failedevent

The creation job failed unexpectedly. Credits are refunded.

2d_render.succeededevent

An asynchronous 2D render finished and result_url is ready.

2d_render.failedevent

An asynchronous 2D render failed.

2d_mockup.ready payload
1{
2 "version": 1,
3 "event": "2d_mockup.ready",
4 "job_id": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
5 "kind": "2d_create",
6 "status": "ready",
7 "created_at": "2026-07-20T10:00:12Z",
8 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
9 "name": "Classic Tee - Front",
10 "print_areas": [
11 {
12 "print_area_id": "223e4567-e89b-12d3-a456-426614174001",
13 "points": [[420, 560], [1580, 560], [1600, 1840], [400, 1840]],
14 "sort_order": 0
15 }
16 ]
17}
2d_mockup.rejected payload
1{
2 "version": 1,
3 "event": "2d_mockup.rejected",
4 "job_id": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
5 "kind": "2d_create",
6 "status": "rejected",
7 "created_at": "2026-07-20T10:00:12Z",
8 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
9 "name": "Classic Tee - Front",
10 "reason": "The image is not suitable for mockup generation."
11}
2d_mockup.failed payload
1{
2 "version": 1,
3 "event": "2d_mockup.failed",
4 "job_id": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
5 "kind": "2d_create",
6 "status": "failed",
7 "created_at": "2026-07-20T10:00:12Z",
8 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
9 "name": "Classic Tee - Front"
10}
2d_render.succeeded payload
1{
2 "event": "2d_render.succeeded",
3 "job_id": "c315f78f-d2c7-4541-b240-a9372842de94",
4 "kind": "2d_render",
5 "status": "succeeded",
6 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
7 "result_url": "https://cdn.sudomock.com/mockup-assets/renders/2d/result.png",
8 "error": null,
9 "created_at": "2026-07-20T10:00:12Z",
10 "export_format": "png",
11 "duration_ms": 1180
12}
2d_render.failed payload
1{
2 "event": "2d_render.failed",
3 "job_id": "c315f78f-d2c7-4541-b240-a9372842de94",
4 "kind": "2d_render",
5 "status": "failed",
6 "mockup_id": "9b0a74cd-0ebb-4748-a748-976fec8cc000",
7 "result_url": null,
8 "error": {
9 "error_code": "RENDER_FAILED",
10 "message": "The mockup could not be rendered. Please try again."
11 },
12 "created_at": "2026-07-20T10:00:12Z"
13}

Handle retries safely

A delivery may arrive more than once. Match on job_id and the event name so a repeated delivery is a safe no-op.

Error Responses

400Bad Request

Invalid input, such as both or neither image source on create, or a print area outside the source image.

json
1{
2 "error_code": "INVALID_SOURCE",
3 "message": "Provide exactly one of source_url or source_base64.",
4 "detail": "Remove one source field, or add the missing source field.",
5 "success": false
6}
401Unauthorized

Missing or invalid API key.

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

Not enough credits. Create needs 25 credits, render needs 5. The response includes actionable links and the credit reset time.

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}
404Not Found

The mockup UUID does not exist or is not owned by your account.

json
1{
2 "detail": "Failed to retrieve mockup",
3 "success": false
4}
409Conflict

Print areas can only be changed after the mockup is ready, or an Idempotency-Key was reused with a different request body.

json
1{
2 "error_code": "MOCKUP_NOT_SETTABLE",
3 "message": "Print areas can only be changed after the mockup is ready.",
4 "suggestion": "Wait for the creation job to finish and try again."
5}
429Too Many Requests

Rate limit or concurrent render limit exceeded. Retry after the indicated delay.

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 "retry_after": 30,
7 "resource": "api"
8 }
9}

Try It Live

POST/api/v1/sudoai/2d-mockups/your-mockup-uuid/render

Render artwork onto a 2D mockup you already created.

Get your API key from the Dashboard

Path Parameters