Batch lifecycle
A batch moves from a manifest you can check for free, to a quote you approve, to files you download. Nothing runs until you approve a quote, and you approve exactly the plan you were quoted.
The steps
- Lint.
POST /projects/{slug}/lintchecks the manifest and its inputs and returns every issue. It answers 200 withvalid: falsefor a bad manifest, not an error status. It is free and does not change anything. - Quote.
POST /projects/{slug}/batches/quoteplans the work and prices it. The reply hastotalCredits, a plan per file,issues,unpricedUnits,approvable, andplanSha256, a hash of exactly this plan. - Approve.
POST /projects/{slug}/batcheswith the manifest,planSha256from the quote,maxCreditsand anIdempotency-Keyheader. The server plans again. If the plan differs from the one you approved it refuses with409 quote_changed; if the cost is overmaxCreditsit refuses with402 credit_ceiling_exceeded. WithdryRun: trueit returns a quote and creates nothing. A new batch is 201; a replay of the same approval is 200 withcreated: false. - Run. The batch moves through
queued,runningandpackaging. Follow it with polling. - Deliver. At the end, read
result.jsonand downloadresult.zip. Check the ZIP's hash against the one inresult.jsonbefore you install anything. - Review. Open the files in the game, then send what you found with
POST /projects/{slug}/reviews.
Refusals at approval
The server checks these at submit, in this order, and answers with the first that applies.
| Status and code | Meaning |
|---|---|
422 idempotency_key_required | No usable Idempotency-Key. |
422 quote_required | planSha256 or maxCredits is missing. |
409 lane_paused_for_problem | A lane is paused for your account. Other lanes still submit. |
409 quote_changed | The plan no longer matches planSha256. |
402 credit_ceiling_exceeded | The quote is over maxCredits. |
402 key_batch_credit_cap, key_daily_credit_cap | The quote is over the key's caps. |
402 project_budget_exceeded | The quote does not fit the project budget. |
422 partial_plan_refused | allowPartial is false and some file cannot be delivered. |
429 plan_submit_limit, active_batch_limit | A plan limit. Retry after the Retry-After header. |
allowPartial
options.allowPartial defaults to true. With it true, a file that cannot be made is left out and the rest of the batch runs, so a batch can end completed_with_failures. With it false, a plan with any unavailable file is refused at submit, and a file that fails during the run stops the batch as needs_review with no ZIP.
Batch states
| State | Active | Meaning |
|---|---|---|
queued | Yes | Accepted and waiting for the runner. |
running | Yes | Files are being made. |
packaging | Yes | Every file has finished and the result is being built. |
completed | No | Every file was delivered or needed no work. result.json and the ZIP are ready. |
completed_with_failures | No | The batch ended and at least one file failed or is unsupported. The files that succeeded are in the ZIP. |
needs_review | No | The batch stopped without a ZIP: allowPartial was false and a file failed, or the batch hit a failure it cannot recover from. status reason says which. |
canceling | Yes | A cancel was requested and running work is being stopped. |
canceled | No | The batch was canceled. |
expired | No | The batch expired before delivery. Its result answers 410 batch_expired. |
awaiting_quote | No | Waiting for a new approval. A free fix batch parks here when its allowances are used up. |
Active states count toward the active batch limit. A batch expires 14 days after it is created. /wait returns at once for a state that is not active.
File states
Each file is worked as a unit. The batch status shows counts by these states, and GET /batches/{batchId}/receipt lists every unit with its state and reason.
| State | Meaning |
|---|---|
waiting | Waiting for a file it depends on. |
ready | Can run. It may wait for its turn, or for the delay before an automatic retry. |
running | Being made. |
admitted | A model job that is queued with a remote worker. |
delivered | Made, checked and in the result. |
failed | Could not be made or did not pass its checks. It is not charged. |
skipped | Nothing to run for it, for example a file that already exists. |
unsupported | Forge cannot make this file here. The reason says why, for example generation_lane_not_enabled. It is not charged. |
canceled | Stopped by a cancel. |
Automatic fixes and best effort
Where automatic fixes are switched on, a generated file that ends in a failure a retry could cure is queued again as a new attempt with a different seed, up to 2 retries, so up to 3 generations in all. Retries add no credits.
If the retries run out and some attempt produced a technically valid file that failed only soft checks (audio peak or clipping), the latest such file is delivered and its metadata says quality: best_effort_after_fixes with the failed checks. Look for that value in the asset files and decide whether to use the file. If no attempt produced a valid file, the unit fails and is not charged, and new submits for that lane from your account answer 409 lane_paused_for_problem until the problem is cleared. Other lanes keep working.
Reviews and free fixes
A review is a myrlin.asset-review/1 document. It pins the exact file versions you looked at and records a verdict per asset with defects and evidence. Upload screenshots and logs first with POST /projects/{slug}/evidence and pass the evidenceSetId. Where fixes are switched on, a review of a broken or unusable file can queue a free fix batch that regenerates only what you named. When its free allowances are used up the fix waits in awaiting_quote; there is no route yet to approve a priced fix.
Cancel, expiry and retention
POST /batches/{batchId}/cancelmoves an active batch tocancelingand thencanceled. A batch waiting for approval is canceled at once. Credits for files that were not delivered are released.- The result ZIP is at most 128 MB and is kept 30 days. After that
result.zipanswers410 result_expired. result-urlreturns a link to the stored ZIP that lasts at most 15 minutes.- Previews are served by
preview.pngand are not inside the ZIP.