MAKEUGC PLATFORM API · DOCS V2
Jobs & webhooks
Match every generation request to its status endpoint, handle terminal states and use notifications alongside polling.
Job lifecycle
- Submit a generation request once and store its job ID.
- Poll the matching status endpoint every 5–10 seconds.
- Stop when the job fails or completes with a non-empty output URL.
- Save the output in your own storage if you need lasting access.
status: true.
Endpoint map
| Generation | ID → status route | State / output |
|---|---|---|
| Talking actors |
data.id → /video/status?id=…
|
data.status / data.url
|
| Product in hand |
data.id → /product-in-hand/status?id=…
|
data.status / data.url
|
| Content Library |
data.db_id →
/content-library/status?id=…
|
data.status / data.url
|
| Shopify templates |
data.db_id →
/content-library/shopify/status?id=…
|
data.status / data.url
|
| Shoppable images / videos |
data.id → /shopable/images/status?id=…
or /shopable/videos/status?id=…
|
data.status / data.url
|
| Dedicated Seedance |
data.id → /seedance/status?id=…
|
data.status / data.url
|
| Enterprise models |
request_id →
/enterprise/status/…?media=image|video
|
status / output.output_link[] or
video_url
|
Most feature status values are processing,
completed, or failed. Enterprise status is
normally 0, 1, or 2; video status
can also be a numeric or named string. Custom avatar creation returns
avatar details directly.
Feature jobs and immediate results
Ad Remake, Gestures, Image Ads, Video Extend, Background Remover and
Templates return data.id. Poll the corresponding feature's
/status?id=ID endpoint. Video outputs use
data.video_url, Image Ads uses data.image_urls
, and Background Remover uses data.output_url.
Custom Voice status returns data.voice_id when the voice is
ready. Scripts, speech text tools, ad analysis, prompt suggestions and
speech audio return their results immediately.
Upscale requires billing_id and the original video
identifiers at /upscale/status. Poll while the attempt is
preparing or charged; stop on
completed, refunded or failed.
Only a completed attempt exposes the new video URL.
These feature endpoints use polling and reject webhook_url.
Check each feature reference for its exact response shape.
Bounded polling
This server-side example polls an enterprise job for up to ten minutes, backs off between reads and handles temporary GET failures. Keep the job ID when a local timeout occurs so you can resume later.
// Server-side Node.js 20+. GET retries only; never resubmit generation here.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");
const base = "https://app.makeugc.ai/api/platform";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function waitForEnterpriseJob(requestId, media) {
const deadline = Date.now() + 10 * 60_000;
let delayMs = 5_000;
while (Date.now() < deadline) {
let response;
try {
response = await fetch(
base + "/enterprise/status/" + encodeURIComponent(requestId)
+ "?media=" + encodeURIComponent(media),
{ headers: { "X-Api-Key": apiKey },
signal: AbortSignal.timeout(Math.min(30_000, deadline - Date.now())) },
);
} catch {
await sleep(Math.min(delayMs, Math.max(0, deadline - Date.now())));
delayMs = Math.min(10_000, delayMs * 1.5);
continue;
}
if ([429, 502, 503, 504].includes(response.status)) {
const retryAfter = response.headers.get("retry-after");
const seconds = Number(retryAfter);
const retryMs = retryAfter
? (Number.isFinite(seconds) ? seconds * 1000 : Date.parse(retryAfter) - Date.now())
: delayMs;
const waitMs = Number.isFinite(retryMs) ? Math.max(delayMs, retryMs) : delayMs;
await sleep(Math.min(waitMs, Math.max(0, deadline - Date.now())));
continue;
}
if (!response.ok) {
throw new Error("HTTP " + response.status + ": " + await response.text()
+ " (request " + response.headers.get("x-request-id") + ")");
}
const job = await response.json();
const state = String(job.status).toLowerCase();
if (["2", "failed"].includes(state)) throw new Error(job.error || "Generation failed");
const urls = media === "image" ? job.output?.output_link : [job.video_url];
const output = (urls || []).filter((url) => typeof url === "string" && url.length > 0);
if (["1", "completed"].includes(state) && output.length) return output;
if (!["0", "1", "queued", "processing", "completed"].includes(state)) {
throw new Error("Unexpected job state: " + state);
}
await sleep(Math.min(delayMs, Math.max(0, deadline - Date.now())));
delayMs = Math.min(10_000, delayMs * 1.2);
}
// Preserve the ID so you can resume polling. Do not create a replacement job.
throw new Error("Polling timed out; resume with request ID " + requestId);
}
const urls = await waitForEnterpriseJob("REQUEST_ID", "image");
console.log(urls);
Webhooks
Several generation endpoints accept webhook_url. Use a
public HTTPS endpoint that accepts JSON POST requests. Persist
notifications and acknowledge them promptly; make your processing
tolerant of duplicates and out-of-order delivery.
-
Talking actors, Product in hand, Content Library, Shopify templates
and dedicated Seedance accept
webhook_url. -
Shoppable images also accept
webhook_secret. Shoppable video generation has no webhook input. -
The enterprise request envelope accepts
webhook_url, but enterprise Seedance does not forward it. Use the dedicated Seedance API or polling. -
Model references marked polling-only reject
webhook_urlbefore submission. This includes the expanded image catalog, Kling, WAN, MiniMax, Pixverse, Seedance 1.5 and the original Grok Video models. Follow the individual reference.
Keep polling as a fallback. Never put your MakeUGC API key in a callback URL. For an integration that requires a verified callback contract, confirm the endpoint-specific details with API support.
Output files
Output links may be signed or temporary. Download completed assets promptly and store them if you need durable access. No universal retention period is specified by these API routes. Keep your input media accessible throughout processing.