Generation status
Start a generation with /api/generate/start, poll GET /api/generate/status, and read the finished model URL back — with the real auth model and expiry rules.
A real generation takes longer than a proxy will hold a request open, so the working pattern is two calls: POST /api/generate/start hands you a jobId, and GET /api/generate/status?jobId= tells you where it got to. When the job finishes, its result field is the complete /api/generate response body, model URL included.
Start a job#
/api/generate/startSigned inQueue a generation and return immediately. Takes the same body as /api/generate.
The raw body and content type are captured and replayed against the generation pipeline in the background, so both the JSON text form and the multipart image form work unchanged. See /docs/api/generate for the fields. An empty body is 400 invalid body.
{ "jobId": "job_m2k1r8q_4f7a9c2e", "status": "queued" }Poll it#
/api/generate/status?jobId=Signed inReturn the job record, including the full generation result once it is done.
jobIdstringrequired- The id returned by /api/generate/start. Jobs are owned by the account that created them; asking for someone else's is 403.
{
"id": "job_m2k1r8q_4f7a9c2e",
"userEmail": "[email protected]",
"status": "done",
"createdAt": 1757404800000,
"startedAt": 1757404800412,
"completedAt": 1757404838907,
"result": { "modelUrl": "gs://…", "printStatus": "ready", "creditsCharged": 1, "…": "…" }
}The three timestamps are epoch milliseconds. startedAt, completedAt, result and error are only present once they apply.
| status | Meaning | What is set |
|---|---|---|
| queued | Accepted, not picked up yet. | createdAt only. |
| running | The pipeline is working — enhancing, generating, downloading, repairing, measuring. | startedAt. |
| done | Finished. Read result. | completedAt, result. |
| error | The generation failed, or the instance running it went away. Credits charged by a failed generation are refunded. | completedAt, error. |
error carries the message the generation route returned — an engine failure, a moderation refusal, a no_credits rejection — or "Generation was interrupted and did not finish." for a job swept after 30 minutes.
A polling loop#
JOB=$(curl -s https://x3dstudios.com/api/generate/start \
-H "Authorization: Bearer $X3D_EXT_TOKEN" \
-H "content-type: application/json" \
-d '{"prompt":"a low-poly owl figurine","mode":"fast_draft"}' | jq -r .jobId)
while :; do
BODY=$(curl -s "https://x3dstudios.com/api/generate/status?jobId=$JOB" \
-H "Authorization: Bearer $X3D_EXT_TOKEN")
case "$(echo "$BODY" | jq -r .status)" in
done) echo "$BODY" | jq -r '.result.modelUrl, .result.printStatus'; break ;;
error) echo "$BODY" | jq -r .error; exit 1 ;;
esac
sleep 5
doneFive seconds is a sensible interval. Status reads are not rate-limited, but nothing changes faster than the pipeline's own two-second engine poll, and a text draft rarely finishes in under twenty seconds.
Getting the model#
result.modelUrl is a gs:// object URI in our storage, not a public link. Fetch it through the proxy, which checks that the signed-in account owns that generation.
curl -G "https://x3dstudios.com/api/model-proxy" \
--data-urlencode "url=$MODEL_URL" \
--data-urlencode "download=true" \
-b "$COOKIE_JAR" -o model.glb| Field in result | Lifetime |
|---|---|
| modelUrl with persisted: true | A gs:// object we wrote. No expiry. This is the repaired, welded, oriented mesh — the one to print. |
| modelUrl with persisted: false | Storage failed, so this is the engine's own pre-signed URL. It expires within the hour and the model is then unrecoverable. Download it now. |
| previewUrl | The engine's original textured mesh, stored alongside. Repair drops UVs, so this is what a viewer should render. Equal to modelUrl when nothing was repaired. |
Is anything running?#
/api/generate/activeSigned inThe account's most recent queued or running job, if there is one.
Session cookie only — this one has no extension-token support. It never returns 401: signed out gives 200 {"active":false}. Signed in with a live job gives {"active":true,"jobId","status","createdAt"}. Jobs older than 30 minutes are ignored here, so a job orphaned by a lost instance cannot block the account forever.
Errors#
| Status | error | Endpoint |
|---|---|---|
| 401 | Authentication required | start and status. No session and no valid x3d_ext_ token. |
| 400 | invalid body | start, when the request body is empty. |
| 400 | jobId required | status, when the query parameter is missing. |
| 404 | not found | status, for an unknown job id. |
| 403 | forbidden | status, when the job belongs to another account. |
| 429 | Generation limit reached. Please try again later. | start. 20 an hour per account, with a Retry-After header. |
Questions
Are there webhooks for finished generations?
No. There is no generation webhook anywhere in X3D. Completion is discovered by polling GET /api/generate/status?jobId=. The only webhooks in the product are Stripe payment webhooks, which have nothing to do with generation.
Does the model URL expire?
It depends on persisted. When persisted is true the URL is a gs:// object in our storage and does not expire. When persisted is false, storage failed and you are holding the engine's own pre-signed URL, which expires within the hour — download it immediately.
How long can a generation take?
The pipeline polls its engine every two seconds with a 15-minute deadline, plus download, repair and validation afterwards. Most draft text generations finish in well under a minute. A job still queued or running 30 minutes after it was created is swept to error.
Related guides
- AI-Generated 3D Models for Printing: What Actually WorksAI-generated 3D models for printing: why they fail, the prompts that fix it, and what any AI 3D model generator for 3D printing has to check.
- AI 3D Model Quality: Why Prints Look UncannyAI 3D model quality, tested in plastic: why AI 3D models look right on screen and wrong in your hand, from baked textures to the 0.4 mm feature floor.
- How AI Text-to-3D Model Generation Works: Prompt to MeshHow does AI text-to-3D model generation work? Prompt enhancement, a generative model, mesh extraction, texturing, and the repair that makes it printable.