Why async?
Audio generation and adaptation can take anywhere from a few seconds to a minute or so. Holding an HTTP connection open for that long is fragile — proxies time out, mobile networks drop, retries become ambiguous. Soundpiece returns immediately from everyPUT with a handle, and you fetch the result when it’s ready.
The same pattern applies to every endpoint, so once you’ve learnt it for one capability you’ve learnt it for all of them.
The lifecycle
Every operation moves through three statuses:outputs / output and error are mutually exclusive — exactly one or the other is populated when the operation terminates.
Submitting work
EveryPUT accepts an idempotency_key — a UUID you generate. Sending the same request twice with the same key returns the original response without doing the work again. This is the safe way to retry over an unreliable network.
id is what you’ll use to fetch the result. The request fields are echoed back so the operation is self-describing without your own bookkeeping.
Polling
Call the same endpoint withGET and the operation id. Pass ?wait=true to long-poll: the server returns as soon as the operation reaches ready or failed, or after 20 seconds if it’s still running.
processing after 20 seconds, just call again — the request is cheap to repeat.
The reference client’s run function does this loop for you, with a deadline.
Polling is the canonical source of truth. Webhooks are a latency optimisation on top — if you don’t receive a webhook within a reasonable window, fall back to polling.
Idempotency end-to-end
Idempotency carries through more than just retries:- The
idempotency_keyis echoed on every response and webhook payload — so you can correlate work end-to-end against your own records without a request-id-to-state map. - 5xx responses on
PUTendpoints are safely retryable — internal retry logic guarantees that the sameidempotency_keylands at most once. You’ll never accidentally create two operations from one logical request.
Handling failures
When an operation itself fails (model rejection, source unreachable, content-policy violation, etc.) you get backstatus: "failed" with a populated error:
error.code values per endpoint.
Downloading the result
Whenstatus is ready, each entry in outputs (or output) carries a signed URL you can GET to download the audio as FLAC (44.1 kHz). The URL is time-limited; check expires_at. If a URL has expired, call the GET endpoint again — we’ll generate a fresh one.
Putting it together
The whole lifecycle, for any endpoint, is three kinds of request. 1. Submit. Choose anidempotency_key and store it with your own record of the job, then send:
200 response holds one key named for the result (song, auto_cut, ducked_audio and so on), with the operation’s id and status: "processing".
2. Wait. Repeat until status is no longer processing:
status is ready, download each output’s url with a plain GET (no Authorization header) before its expires_at. If it is failed, read error.code.
At any step, a network error, a 429 or a 5xx means: wait (for Retry-After seconds, when present) and send the same request again. On a submit, the same idempotency_key guarantees the work is only done once. Any other 4xx means the request needs fixing.
The reference client is exactly this loop in one small file, and the Quickstart shows it in use. For production, receive results by webhook and keep polling as the fallback.