remove-background is synchronous: no polling at all. tighten and
launch-cut are queued and resolve through the jobs envelope.
Job states
queued → running → completed | refused | failed
completedcarries the full envelope:finalUrl,canvasUrl,recipeVersion, timings.refusedcarries a typed reason: free.failedis on us: see Infrastructure errors; failed jobs are not charged.
Estimate first
GET /v1/estimate?recipeId={id} is free and returns
{ credits, etaSeconds, creditsRemaining, blockers[] } with typed blockers
(INSUFFICIENT_CREDITS, IN_FLIGHT, SCOPE_DENIED,
RECIPE_NOT_ALLOWED, SPEND_CAP_REACHED). Agents should estimate, ask consent, then run.
An estimate validates the PRICE and your ACCOUNT (credits, scopes, in-flight
caps), not your material: material checks (duration, readability, subject
presence) happen at run time, and when they fail you get a free typed
refusal, never a charge.
Polling etiquette
Poll every 5-15s; each template page posts anetaSeconds to calibrate
expectations. Webhooks (callback_url) are on the roadmap for
fire-and-forget integrations.