> For the complete documentation index, see [llms.txt](https://docs.projectsim.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.projectsim.ai/guides/jobs-and-assets.md).

# Jobs & assets

Asset, variation-set, and batch creation is asynchronous. Creating any of these returns `202` with a `job_id`; the work runs in the background; you poll, then download from the finished resource.

## Two handles, two questions

An **asset** is the durable resource. A **job** is one execution against a resource — the public status surface. They answer different questions:

* **Poll the job** for the run you just started. A `job_id` names one execution and always resolves to that run, so it never goes stale.
* **Poll the asset** (`GET /v1/assets/{asset_id}`) for where the asset has got to now. A resource can have more than one job over time; the asset always reports its current one.

Assets have no status of their own — the asset's latest job is inlined on reads.

## Job lifecycle

```mermaid
stateDiagram-v2
    [*] --> queued
    queued --> running
    running --> complete
    running --> failed
    queued --> failed
    queued --> canceled
    running --> canceled
    complete --> [*]
    failed --> [*]
    canceled --> [*]
```

* `complete`, `failed`, `canceled` are terminal. `partial` is terminal too, but applies to [variation sets](/creating-assets/high-fidelity-variation-set.md) and [batches](/creating-assets/batches.md) — inspect member counts for failures or cancellations.
* While running, `current_stage` is one of `variation`, `reconstruction`, `scale` or `physics`.

Cancel a job with `POST /v1/jobs/{job_id}/cancel` before generation starts. After that the API returns `409` and generation continues. Cancel a variation set or batch through its parent job before any member starts. Variation-set members cannot be canceled individually; batch members can, before they start. Repeating a cancellation of an already canceled job returns it unchanged.

A job that is still `queued` or `running` when its time budget runs out moves to `failed`; the budget is measured from creation with queue time included.

For event notifications instead of a continuous poll loop, see [Webhooks](/guides/webhooks.md). Fetch the resource after a notification to read its current state and download URLs.

## Account limit

Usage is bounded by your plan's credits, not by an asset count. An account given a lifetime asset limit (a free beta key, for example) may create at most that many assets. Every asset counts — standalone or a variation-set member, finished, failed or deleted — so a set of 100 spends 100 of the limit, and deleting assets does not free it. The one exception is a canceled asset: nothing was generated, so it does not count. A create that would cross the limit is refused with a `429` of type `asset-limit-reached` before anything is written; a replayed `Idempotency-Key` still returns its original response.

## Idempotency

Asset, variation-set, and batch create calls accept an `Idempotency-Key` header. A replay returns the original response instead of creating a second resource; reusing a key with a different body is a `409`. Always send one — a timed-out retry without a key can duplicate the whole order.

## Downloads

Download URLs are minted per response and short-lived:

| Surface                         | URL lifetime |
| ------------------------------- | ------------ |
| `GET /v1/assets/{id}` files     | 1 hour       |
| Variation set member files      | 24 hours     |
| Set manifest                    | 24 hours     |
| Batch member files and manifest | 24 hours     |

Never cache a presigned URL — re-read the resource to mint a fresh one.
