Async Rendering
Submit a render or PSD upload and get an immediate acknowledgement instead of waiting for the result. Poll the job for its outcome, or let a webhook tell you when it finishes.
Sync vs Async
By default, POST /api/v1/renders and POST /api/v1/psd/upload are synchronous: the request blocks until the work is done and returns the result inline with a 200OK. That is the simplest path and works well for a single render in a request/response cycle.
For long-running work, large batches, or any flow where you do not want to hold a connection open, run the same endpoints asynchronously. Add "is_async": true to the request body and you get an immediate 202Accepted with a render_uuid you can track to completion.
| Synchronous (default) | Asynchronous (is_async: true) | |
|---|---|---|
| Response | 200 with the result inline | 202 with a render_uuid + status_url |
| Connection | Held open until the work finishes | Returns immediately |
| How you get the result | In the response body | Poll the job, or receive a webhook |
| Best for | A single render in a request cycle | Batches, long jobs, queue-driven pipelines |
Same endpoints, one flag
is_async on the existing POST /api/v1/renders and POST /api/v1/psd/upload requests. Video generation is always asynchronous and returns a 202 with no flag needed; see Render Video.The is_async flag
is_asyncboolean= falseSet to true to process the request in the background. The endpoint responds with 202 Accepted and a render_uuid instead of the inline result. Defaults to false (synchronous).
1{2 "is_async": true,3 "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",4 "smart_objects": [5 {6 "uuid": "128394ee-6758-4f2f-aa36-e2b19b152bd9",7 "asset": {8 "url": "https://your-domain.com/design.png",9 "fit": "cover"10 }11 }12 ],13 "export_options": {14 "image_format": "webp",15 "image_size": 1920,16 "quality": 9017 }18}
export_options carry over unchanged
is_async: true; all the same export_options apply. The example sets image_size: 1920, which overrides the default of 2048. See the Render Mockup reference for the full export_options list.The 202 lifecycle
An async submission returns 202Accepted right away. The body echoes back enough to start tracking the job:
1{2 "render_uuid": "9d4e2b51-0c7a-4f8e-bb1c-2a6f9e3d8c10",3 "kind": "render",4 "status": "queued",5 "status_url": "/api/v1/jobs/9d4e2b51-0c7a-4f8e-bb1c-2a6f9e3d8c10"6}
202 Response Fields
render_uuidstringRequiredThe job identifier. Pass it to GET /api/v1/jobs/{render_uuid} to track progress and retrieve the result.
kindstringRequiredWhat the job produces: "render", "video", or "upload". When the job finishes, "render" and "video" expose the downloadable result_url; "upload" exposes a mockup_uuid plus a result_url that deep-links to the new mockup.
statusstringRequiredInitial state, always "queued" on a fresh submission. See the Jobs reference for the full state machine.
status_urlstringRequiredRelative path to poll for this job, equal to /api/v1/jobs/{render_uuid}. Resolve it against the API base URL.
A PSD upload submitted with is_async returns the same shape, with kind set to upload:
1{2 "render_uuid": "1f0a8c23-7b44-4e90-9c2d-55ab1e7f0d33",3 "kind": "upload",4 "status": "queued",5 "status_url": "/api/v1/jobs/1f0a8c23-7b44-4e90-9c2d-55ab1e7f0d33"6}
Submit, then poll
The async pattern is two steps: submit to get a render_uuid, then poll GET /api/v1/jobs/{render_uuid} until the status is succeeded, failed, or cancelled. Use a gentle backoff so you are not hammering the endpoint.
A job moves through a fixed set of states. The full state machine is documented in the Jobs reference; the states you will observe are:
queued: accepted and waiting to start. This is the initial state returned with the 202Accepted.processing: the job is actively running.succeeded: terminal. The result is ready;renderandvideojobs expose aresult_url, anduploadjobs expose amockup_uuid.failed: terminal. The job did not complete;errordescribes why.cancelled: terminal. The job was cancelled before it finished.
queued and processing are non-terminal: keep polling. succeeded, failed, and cancelled are terminal: stop polling.
1const BASE_URL = "https://api.sudomock.com/api/v1";2const headers = {3 "Content-Type": "application/json",4 "x-api-key": "sm_your_api_key",5};67// 1) Submit asynchronously -> 202 Accepted8const submit = await fetch(`${BASE_URL}/renders`, {9 method: "POST",10 headers,11 body: JSON.stringify({12 is_async: true,13 mockup_uuid: "c315f78f-d2c7-4541-b240-a9372842de94",14 smart_objects: [{15 uuid: "128394ee-6758-4f2f-aa36-e2b19b152bd9",16 asset: { url: "https://your-domain.com/design.png", fit: "cover" },17 }],18 }),19});2021const { render_uuid } = await submit.json();2223// 2) Poll the job until it is finished24async function poll(uuid, { intervalMs = 1500, maxMs = 120000 } = {}) {25 const deadline = Date.now() + maxMs;26 let delay = intervalMs;27 while (Date.now() < deadline) {28 const res = await fetch(`${BASE_URL}/jobs/${uuid}`, { headers });29 const job = await res.json();30 if (job.status === "succeeded") return job.result_url;31 if (job.status === "failed" || job.status === "cancelled") {32 throw new Error(`Job ${job.status}: ${job.error ?? "unknown"}`);33 }34 await new Promise(r => setTimeout(r, delay));35 delay = Math.min(delay * 1.5, 8000); // gentle backoff, cap at 8s36 }37 throw new Error("Timed out waiting for the job to finish");38}3940const resultUrl = await poll(render_uuid);41console.log("Done:", resultUrl);
Skip polling with webhooks
SDKs cover async
Official SDKs handle submit + poll for you
sudomock npm package (npmjs.com/package/sudomock) and the sudomock PyPI package (pypi.org/project/sudomock) cover renders, async submission, video, jobs, and webhooks. They wrap the submit-and-poll loop so you can await a finished result directly.