Runs and creatives
Follow a run through review, queue and render; approve, re-render, download and delete creatives.
A run is one call of a flow's API Trigger. It owns the creatives that call built, and its state follows them.
Reading a run
GET /runs/<run_id> answers the run derived from its creatives at that moment:
{
"run": {
"id": "...",
"run_version": 17,
"status": "rendering",
"error": null,
"external_id": "customer-job-48291",
"notice": null,
"auto_queue": false,
"adopted": { "count": 0, "creative_ids": [] },
"counts": { "total": 3, "review": 0, "queued": 1, "rendering": 1, "rendered": 1, "failed": 0 },
"creatives": [
{
"id": "...",
"creative_id": "summer-sale-uk-a",
"status": "rendering",
"progress": { "stage": "rendering", "percent": 42, "frames_done": 1320, "frames_total": 3147, "started_at": "..." },
"error": null,
"render": null,
"delivery": null
}
],
"completed_at": null
}
}
auto_queue records what the call asked for (true = the render queue, Triggering a flow); when the queue fell back to review, the answer's queue_fallback and the run's notice say why.
run_version is the run's monotonic version: it goes up by one on every state change Nubu records, and every callback carries the version it reports (Callbacks). Compare versions, never timestamps, to know which reading is newer; the list carries it too.
The run's states
| Status | Meaning |
|---|---|
processing | The flow is building its creatives, or a finished render is still being delivered by an API Request node. |
review | Creatives are waiting for approval. |
queued | Creatives are waiting for a render machine. |
rendering | At least one creative is rendering. |
completed | Every creative has finished and at least one rendered; deliveries included. |
failed | The flow could not be built, or every creative failed. |
A run with a failed build, or one where every creative failed, carries a run-level error with a class and a message. A run that completed with some failures carries the failures on the creatives themselves, so a partial failure is always visible per creative.
Each creative
status: the creative's own lifecycle (building,review,queued,processing,rendering,rendered,failed,cancelled).progress: while a render is in flight, the render machine's progress (the stage, the percentage, the frames so far, when it started).error:null, or{ "class", "message" }. The classes distinguish where things went wrong:asset_upload_failed,flow_processing_failed,creative_build_failed,render_failed,delivery_failed.render: the finished output (its id, content type, size, dimensions, duration, when it rendered).delivery: the API Request node's delivery state when the flow has one (pending,uploading,uploaded,failed, with the attempts, the HTTP status and how the render was sent). See API Request delivery.adopted:truewhen the creative was built by an earlier call and this run reports it as its result (below).
GET /runs lists runs, newest first, with flow_id, campaign_id, external_id and status filters; each row carries adopted_count.
Calling a trigger again
A trigger call is safe to repeat: a retry, or the same job sent twice, never renders anything twice.
- Creatives of an earlier call that are queued, rendering or rendered are left exactly as they are. The new run adopts them: they appear in its
creativeswith"adopted": trueand their current state, the run'sadoptedfield carries their count and ids, and the run's state follows them (it completes once they are rendered, and its callbacks report them).creatives[].idis the same id the earlier run reported, so your records line up; the earlier run keeps them too. - Creatives of an earlier call that are still in review are rebuilt: the new call's rows replace them (the same
creative_idnames, fresh ids) and the earlier run follows the rebuilt rows as its adopted creatives, so both runs report the same creatives. - A later call with a different input that leaves an earlier run with no creatives at all records that run as
failedwith the classcreative_build_failed, saying which call replaced its creatives.
The trigger's 202 answer carries the same adopted field, so a retry that adopted everything can be recognised at once.
Working with creatives
GET /creativeslists creatives (campaign_id,flow_id,run_idandstatusfilters);GET /creatives/<id>adds the progress, the number of render attempts and, once rendered, a 15-minutedownload_url.POST /creatives/<id>/approvesends a creative in review to the render queue, exactly as Approve does in the app. Only creatives in review can be approved; an organisation whose storage is full answers409 conflictwithdetails.reasonstorage_fulluntil space is freed.POST /creatives/<id>/rerendersends a rendered, failed or cancelled creative back to the queue. The previous render stays until the new one lands. While storage is full it answers409 conflictwithdetails.reasonstorage_full, like approve.PATCH /creatives/<id>edits the copy columns (headline,primary_text,description,cta,final_url, theutm_*fields,notes),market,language,version,filenameandfields. A creative that is queued or rendering belongs to the render machine and answers409 conflict.GET /creatives/<id>/downloadanswers a 15-minute URL for the finished render, with its filename, content type and size. A creative without a finished render answers409.DELETE /creatives/<id>removes the creative and its rendered files; one mid-render answers409.POST /creativescreates a creative row outside a flow (creative_idrequired; the same columns as the edit).
The renders themselves always stay stored in Nubu.