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

  1. Lint. POST /projects/{slug}/lint checks the manifest and its inputs and returns every issue. It answers 200 with valid: false for a bad manifest, not an error status. It is free and does not change anything.
  2. Quote. POST /projects/{slug}/batches/quote plans the work and prices it. The reply has totalCredits, a plan per file, issues, unpricedUnits, approvable, and planSha256, a hash of exactly this plan.
  3. Approve. POST /projects/{slug}/batches with the manifest, planSha256 from the quote, maxCredits and an Idempotency-Key header. The server plans again. If the plan differs from the one you approved it refuses with 409 quote_changed; if the cost is over maxCredits it refuses with 402 credit_ceiling_exceeded. With dryRun: true it returns a quote and creates nothing. A new batch is 201; a replay of the same approval is 200 with created: false.
  4. Run. The batch moves through queued, running and packaging. Follow it with polling.
  5. Deliver. At the end, read result.json and download result.zip. Check the ZIP's hash against the one in result.json before you install anything.
  6. 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.

Refusals at approval
Status and codeMeaning
422 idempotency_key_requiredNo usable Idempotency-Key.
422 quote_requiredplanSha256 or maxCredits is missing.
409 lane_paused_for_problemA lane is paused for your account. Other lanes still submit.
409 quote_changedThe plan no longer matches planSha256.
402 credit_ceiling_exceededThe quote is over maxCredits.
402 key_batch_credit_cap, key_daily_credit_capThe quote is over the key's caps.
402 project_budget_exceededThe quote does not fit the project budget.
422 partial_plan_refusedallowPartial is false and some file cannot be delivered.
429 plan_submit_limit, active_batch_limitA 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

Batch states
StateActiveMeaning
queuedYesAccepted and waiting for the runner.
runningYesFiles are being made.
packagingYesEvery file has finished and the result is being built.
completedNoEvery file was delivered or needed no work. result.json and the ZIP are ready.
completed_with_failuresNoThe batch ended and at least one file failed or is unsupported. The files that succeeded are in the ZIP.
needs_reviewNoThe 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.
cancelingYesA cancel was requested and running work is being stopped.
canceledNoThe batch was canceled.
expiredNoThe batch expired before delivery. Its result answers 410 batch_expired.
awaiting_quoteNoWaiting 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.

Unit states
StateMeaning
waitingWaiting for a file it depends on.
readyCan run. It may wait for its turn, or for the delay before an automatic retry.
runningBeing made.
admittedA model job that is queued with a remote worker.
deliveredMade, checked and in the result.
failedCould not be made or did not pass its checks. It is not charged.
skippedNothing to run for it, for example a file that already exists.
unsupportedForge cannot make this file here. The reason says why, for example generation_lane_not_enabled. It is not charged.
canceledStopped 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}/cancel moves an active batch to canceling and then canceled. 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.zip answers 410 result_expired.
  • result-url returns a link to the stored ZIP that lasts at most 15 minutes.
  • Previews are served by preview.png and are not inside the ZIP.