> ## Documentation Index
> Fetch the complete documentation index at: https://docs.soundpiece.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> HTTP error responses and per-operation failure codes

## Why two layers of errors?

Soundpiece returns two distinct kinds of error, and they mean different things:

* **HTTP-level errors** (`4xx` / `5xx`) — your *request* was rejected. The work never started.
* **Operation-level errors** — your request was accepted (you got `200 OK` and an operation `id`), but the *operation itself* failed during processing. You poll the operation just like any other and find `status: "failed"` with a populated `error` object.

This split is deliberate: the request layer is about whether you talked to us correctly, the operation layer is about whether the work succeeded. They have different causes, different retry rules, and different recovery strategies. A successful HTTP response doesn't mean the operation succeeded — you still need to check `status`.

***

## HTTP errors

The two rules of thumb:

* **`4xx` — fix your request.** Something in what you sent doesn't work. Retrying with the same body won't help; you need to change the request.
* **`5xx` — something unexpected happened on our side. You can retry.** `PUT` endpoints are idempotent on `idempotency_key` (see [Async operations](/async-operations#idempotency-end-to-end)), so retrying with the same key is safe — you won't double-create work.

### Response shape

HTTP errors return a JSON body with a `detail` field. For most errors it's a plain string:

```json theme={null}
{
  "detail": "Missing or invalid Authorization header"
}
```

For request-body validation errors (`422`), `detail` is a **list** of per-field entries:

```json theme={null}
{
  "detail": [
    {
      "loc": ["body", "lyrics"],
      "msg": "String should have at least 1 character",
      "type": "string_too_short"
    },
    {
      "loc": ["body", "idempotency_key"],
      "msg": "Field required",
      "type": "missing"
    }
  ]
}
```

| Field  | Meaning                                                                                                                                 |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `loc`  | Path to the offending field — e.g. `["body", "lyrics"]` for a top-level body field, or `["body", "reference", "url"]` for a nested one. |
| `msg`  | Human-readable description of the problem.                                                                                              |
| `type` | Stable machine-readable code (e.g. `missing`, `string_too_short`, `url_parsing`).                                                       |

### Status reference

| Status                     | When you'll see it                                                                                                                                                                  | Recommended handling                                                                                                |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`          | Malformed JSON or unparseable request body.                                                                                                                                         | Fix the request.                                                                                                    |
| `401 Unauthorized`         | Missing, invalid, or revoked API key (and, on Enterprise plans with [IP allowlisting](/authentication#enterprise-ip-allowlisting), a request from a non-allowlisted IP).            | Check your `Authorization` header and source IP. Don't retry.                                                       |
| `402 Payment Required`     | Your account doesn't have enough credits to perform the requested work. See [Insufficient credits](#insufficient-credits-402) below for the response shape.                         | Top up your account, then retry with the same `idempotency_key`.                                                    |
| `404 Not Found`            | **Only** returned when the endpoint URL itself doesn't exist — e.g. a typo in the path. Entity-not-found (operation id, etc.) is reported as a `422` validation error, not a `404`. | Check the URL.                                                                                                      |
| `405 Method Not Allowed`   | Wrong HTTP verb on a known route (e.g. `POST` on a `PUT`-only endpoint).                                                                                                            | Use the documented verb.                                                                                            |
| `410 Gone`                 | The endpoint existed on this API version but has been sunset. The `detail` field points to the replacement.                                                                         | Migrate to the replacement endpoint. See [versioning](/versioning).                                                 |
| `422 Unprocessable Entity` | Request body validation failed — missing field, wrong type, out-of-range value, invalid URL, or a referenced entity (operation `id` etc.) wasn't found.                             | Read `detail` to see which field is bad.                                                                            |
| `429 Too Many Requests`    | You've hit your account's request rate limit.                                                                                                                                       | Read the `Retry-After` header (seconds) and back off. See [rate limits](/rate-limits).                              |
| `5xx`                      | Transient server-side issue.                                                                                                                                                        | **Retry safely** — `PUT` requests are idempotent if you supply the same `idempotency_key`. Use exponential backoff. |

### Insufficient credits (`402`)

When you submit a request whose credit cost exceeds your current balance, the call is rejected up-front (no work starts, no charge) with a `402` whose body is:

```json theme={null}
{
  "detail": {
    "type": "insufficient_credits",
    "msg": "You have insufficient credits to perform this action."
  }
}
```

The per-endpoint credit cost is documented on each endpoint (e.g. `create.remix_song` and `create.remix_instrumental` charge **3 credits per variation**, so `n_variations: 3` costs 9). Top up your account and re-submit with the same `idempotency_key` — the original request was never persisted, so the retry creates the work fresh.

***

## Operation-level failures

When an operation fails, the response looks like this:

```json theme={null}
{
  "song": {
    "id": "op_01h7k...",
    "status": "failed",
    "error": {
      "code": "tos_violation",
      "message": "The provided lyrics were rejected by our content policy."
    },
    ...
  }
}
```

`error.code` is a **stable machine-readable identifier** — safe to branch on. `error.message` is a human-readable description suitable for surfacing to your end-users.

Not every code applies to every endpoint. The sections below group codes by which endpoints can return them.

### Codes returned by all endpoints

| Code                 | Meaning                                                         | Recommended handling                                                        |
| -------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `internal_error`     | Generic failure with no more specific classification available. | Re-submit with the same `idempotency_key`. If it persists, contact support. |
| `generation_timeout` | The operation took longer than our hard timeout to complete.    | Re-submit. Persistent timeouts are worth raising with support.              |
| `quota_exceeded`     | Your account is out of monthly capacity.                        | Top up your account or wait for the next billing cycle.                     |

### Codes for endpoints with a `source` or `reference`

(applies to `adapt.separate_stems`, `adapt.auto_cut_down`, `create.remix_song`, `create.remix_instrumental`, and the `reference` variant of `create.new_song` / `create.new_instrumental`)

| Code                        | Meaning                                                                                        |
| --------------------------- | ---------------------------------------------------------------------------------------------- |
| `source_unreachable`        | We couldn't fetch the URL — DNS failure, connection refused, TLS error, 4xx/5xx from the host. |
| `source_format_unsupported` | The file at the URL is not a supported audio format (we accept wav, mp3, flac).                |
| `source_too_short`          | The fetched audio is too short for this operation (typically under 10 seconds).                |
| `source_too_long`           | The fetched audio exceeds the maximum length for this operation.                               |

### Codes for generation endpoints

(applies to `create.new_song`, `create.new_instrumental`, `create.new_sample`, `create.new_fx`, `create.remix_song`, `create.remix_instrumental`)

| Code            | Meaning                                                               |
| --------------- | --------------------------------------------------------------------- |
| `tos_violation` | The lyrics, prompt, or reference were rejected by our content policy. |

***

## Why retries don't risk double-billing

`PUT` endpoints are **idempotent on `idempotency_key`**. The same key always returns the same operation, regardless of how many times you retry. This means:

* `5xx` HTTP responses are safely retryable — retry with the same key and you'll either get a fresh attempt or the existing successful result.
* `429` is retryable — back off using `Retry-After`, then send the same key again.
* `4xx` other than `429` indicate a bad request that won't get better; fix the request before retrying.

You will never accidentally create two operations from one logical request, no matter how many times the network drops between you and us.

The `idempotency_key` is also **echoed on every response and webhook payload** so you can correlate work against your own records without a separate request-id-to-state map.

***

## Reporting persistent issues

If you receive `internal_error` (operation-level) or `5xx` (HTTP-level) that persists across retries, please contact [business@soundpiece.co.uk](mailto:business@soundpiece.co.uk) with:

* The endpoint called.
* The operation `id` if one was returned.
* The `idempotency_key` you sent.
* The approximate timestamp.
* Any `error.code` / `error.message` from the response body.
