Skip to main content

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 every PUT 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

Every PUT 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.
You get back the operation immediately:
The 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 with GET 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.
If you get back 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_key is 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 PUT endpoints are safely retryable — internal retry logic guarantees that the same idempotency_key lands 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 back status: "failed" with a populated error:
See the error reference for the full catalogue of error.code values per endpoint.

Downloading the result

When status 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.
Don’t cache the URL itself — it’s signed against a short window. Do cache the audio bytes if you need them again.

Putting it together

The whole lifecycle, for any endpoint, is three kinds of request. 1. Submit. Choose an idempotency_key and store it with your own record of the job, then send:
The 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:
Each call returns as soon as the operation finishes, or after 20 seconds. Give up after a deadline of your choosing, or receive a webhook instead. 3. Finish. If 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.