> ## 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.

# create.new_fx (submit)

> Submit an audio effect generation request.

Returns immediately with `status: processing`. Use the matching GET to
poll for completion, or supply a `callback_url` to receive a webhook.

Effects are short, single-shot audio elements like risers, impacts,
transitions or foley generated from a text prompt at the requested
duration. A prompt rejected by our content policy returns the operation
as `failed` with `tos_violation`.

**Credits cost:** 3 per request. Insufficient credits returns `402` —
see [errors](/errors).



## OpenAPI

````yaml /openapi.json put /v1/create.new_fx
openapi: 3.1.0
info:
  title: Soundpiece API
  description: >

    # Soundpiece API


    The Soundpiece API gives you programmatic access to Soundpiece's audio

    generation, separation, and adaptation capabilities. Generate songs from

    lyrics, separate stems from a mix, cut audio to length — anything you can do

    in the Soundpiece app, you can do over HTTP.


    This is an **RPC-style API**, similar in shape to Slack's or Stripe's APIs.

    Each endpoint is a named action like `create.new_song` or

    `adapt.separate_stems`. There are no resource hierarchies to learn — pick
    the

    action you want and call it.


    ## Getting started


    1. Get your API key from your account dashboard. Each key comes with a
    webhook
       signing secret — save both, they're shown once.
    2. Authenticate with `Authorization: Bearer <your-api-key>`.

    3. Submit work with a `PUT` to the action endpoint. You'll get back a
    response
       with `status: processing`.
    4. Poll the same endpoint with `GET` and `?wait=true` to wait up to 20
    seconds
       for the result, or use webhooks to be notified when work is done.

    ### Example: generate a song


    ```http

    PUT /v1/create.new_song

    Authorization: Bearer <your-api-key>

    Content-Type: application/json


    {
      "idempotency_key": "3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b",
      "lyrics": "I've been driving all night, my hands wet on the wheel",
      "prompt": "Synthwave, melancholic, mid-tempo"
    }

    ```


    You'll get back:


    ```json

    {
      "song": {
        "id": "op_01m3vcs7e0f8rsq32d5sqhmetw",
        "status": "processing",
        "created_at": "2026-10-01T09:30:00Z",
        "idempotency_key": "3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b",
        "lyrics": "I've been driving all night, my hands wet on the wheel",
        "prompt": "Synthwave, melancholic, mid-tempo",
        "n_variations": 1,
        "outputs": null,
        "error": null
      }
    }

    ```


    Poll with `GET
    /v1/create.new_song?id=op_01m3vcs7e0f8rsq32d5sqhmetw&wait=true` until
    `status` is

    `ready` (`outputs` populated) or `failed` (`error` populated).


    ## Method shapes


    Each method is either a *command* (writes state) or a *query* (reads state).


    | Kind | Verb | Body | Notes |

    |---|---|---|---|

    | Command (idempotent) | `PUT` | JSON | Used to submit work. Include an
    `idempotency_key` to retry safely. |

    | Query | `GET` | none | Used to poll for operation state. |


    Responses are JSON unless otherwise noted.


    ## Authentication


    All endpoints require an API key passed as a Bearer token:


    ```http

    Authorization: Bearer <your-api-key>

    ```


    Keys start with the prefix `spk_` — the prefix is part of the key, so use
    the

    value exactly as the dashboard shows it. Keep keys secret — anyone with your

    key can use your account.


    You can manage keys in your account dashboard: create new ones, list
    existing

    ones, revoke compromised ones. **The key value and webhook signing secret
    are

    shown once at creation** — save them somewhere safe.


    ## Idempotency


    Every `PUT` request takes an `idempotency_key` field — a UUID you choose. If

    you send the same request twice with the same key, we return the original

    response without doing the work again. This is the safe way to retry over an

    unreliable network.


    The key is also echoed back in every response and webhook delivery, so you

    can correlate work end-to-end on your side without keeping a
    request-id-to-state

    map.


    ## Operation status lifecycle


    Every operation moves through three statuses:


    - **`processing`** — submitted; we're working on it. `output`/`outputs` and
      `error` are both `null`.
    - **`ready`** — done. The `output` (or `outputs`) field is populated.

    - **`failed`** — something went wrong. The `error` field is populated with a
      `code` and `message`.

    `output`/`outputs` and `error` are mutually exclusive — exactly one or the

    other is populated when the operation terminates.


    ## Polling vs webhooks


    Two ways to find out when an operation is done:


    **Polling** — call the same endpoint with `GET` and an `id`. Add
    `?wait=true`

    to long-poll for up to 20 seconds. Repeat if you get back `processing`.


    **Webhooks** — supply a `callback_url` in the original `PUT` request. We

    `POST` the final response to that URL when the operation reaches `ready` or

    `failed`. See the *Webhooks* section below for signature verification.


    **Polling is canonical.** Webhooks are a best-effort latency optimisation;
    if

    you didn't receive one within a reasonable window, fall back to polling.

    Don't rely on webhook receipt as proof of operation state.


    ## Downloads


    When an operation is `ready`, each output carries a signed URL you can use
    to

    download the audio as a FLAC file (44.1 kHz). These URLs are time-limited;

    check the `expires_at` field on each output. If a URL has expired, call the

    matching `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 you downloaded if you need to reuse it.


    ## Webhooks


    If you supply a `callback_url` in a `PUT` request, we'll `POST` the final

    response to that URL when the operation reaches `ready` or `failed`. The

    payload is the same body you'd get from polling, e.g.:


    ```json

    {
      "song": { "...the same DTO you'd get from polling..." }
    }

    ```


    We sign every webhook so you can verify it came from us. Each delivery

    includes these headers (following the [Standard
    Webhooks](https://www.standardwebhooks.com/)

    spec):


    - `webhook-id` — a stable id for this event. Retries reuse the same id, so
      consumers can dedupe.
    - `webhook-timestamp` — Unix seconds when we first attempted delivery.

    - `webhook-signature` — `v1,<base64(HMAC-SHA256(secret,
    "<id>.<timestamp>.<body>"))>`.
      Multiple signatures are space-separated during secret rotation.
    - `webhook-type` — what happened: the endpoint name plus `.ready` or
      `.failed`, e.g. `create.new_song.ready`. Lets you route an event without
      parsing its body. It is not covered by the signature (which is over the
      id, timestamp and body), so treat it as a routing hint and trust the
      verified body.

    Verify by computing the HMAC with your signing secret and comparing in

    constant time. Reject the request if the signature doesn't match, or if the

    timestamp is more than 5 minutes from the current time.


    We retry failed deliveries with exponential backoff. `5xx` responses and

    connection errors get retried; `4xx` responses (other than `429`) fail

    immediately — fix your endpoint and we'll deliver the next one.


    ## Errors


    When an operation itself fails (e.g. your prompt was rejected by our content

    policy), the response has `status: "failed"` with a populated `error`:


    ```json

    {
      "code": "tos_violation",
      "message": "The request was rejected by our content policy."
    }

    ```


    HTTP-level errors (`4xx` / `5xx`) return a JSON body with a `detail` field —

    either a string message, or a list of `{loc, msg, type}` entries pinpointing

    which field of the request body failed validation. `5xx` responses on `PUT`

    endpoints are safely retryable — the same `idempotency_key` will land at

    most once.


    ## Versioning


    We avoid breaking changes to request and response shapes. New fields will

    appear on responses additively; deprecated fields will be marked as such
    with

    notice before removal. If an unavoidable breaking change is ever needed, it

    will be on a versioned URL path so existing clients keep working.
  version: 0.1.0
servers:
  - url: https://api.soundpiece.co
    description: Production
security:
  - Bearer: []
tags:
  - name: Create
    description: >

      Generate new audio from a prompt, reference track, or lyrics.


      Each endpoint follows the same pattern: `PUT` to submit work, `GET` to
      poll.

      The `PUT` returns immediately with `status: processing`; the `GET` returns
      the

      same DTO with updated status until it reaches `ready` or `failed`.


      Generation can take anywhere from a few seconds to a minute or so
      depending

      on length and current capacity. Use `?wait=true` on the `GET` to long-poll

      for up to 20 seconds, or supply a `callback_url` on the `PUT` to be
      notified.
  - name: Adapt
    description: >

      Transform existing audio — separate stems, cut to a target duration,
      normalise

      to a target loudness (in LUFS, or by a named preset such as `podcast`),
      mix a

      voice over music with the music turned down while the voice speaks.


      These endpoints take their audio inputs as URLs (see the `AudioSource`
      schema)

      and produce a derived output. The same async-status pattern as `create.*`

      applies.
paths:
  /v1/create.new_fx:
    put:
      tags:
        - Create
      summary: create.new_fx (submit)
      description: |-
        Submit an audio effect generation request.

        Returns immediately with `status: processing`. Use the matching GET to
        poll for completion, or supply a `callback_url` to receive a webhook.

        Effects are short, single-shot audio elements like risers, impacts,
        transitions or foley generated from a text prompt at the requested
        duration. A prompt rejected by our content policy returns the operation
        as `failed` with `tos_violation`.

        **Credits cost:** 3 per request. Insufficient credits returns `402` —
        see [errors](/errors).
      operationId: create.new_fx
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateNewFxRequest'
            examples:
              Keyboard typing:
                value:
                  idempotency_key: 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
                  prompt: someone typing on a keyboard
                  duration: 5
              Cinematic impact:
                value:
                  idempotency_key: 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
                  prompt: a deep cinematic impact with a long metallic tail
                  duration: 4
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateNewFxResponse'
              examples:
                Just submitted:
                  value:
                    fx:
                      id: op_01m3vcs7e0f8rsq32d5sqhmetw
                      status: processing
                      created_at: '2026-10-01T09:30:00Z'
                      idempotency_key: 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
                      prompt: someone typing on a keyboard
                      duration: 5
                      output: null
                      error: null
                Ready:
                  value:
                    fx:
                      id: op_01m3vcs7e0f8rsq32d5sqhmetw
                      status: ready
                      created_at: '2026-10-01T09:30:00Z'
                      idempotency_key: 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
                      prompt: someone typing on a keyboard
                      duration: 5
                      output:
                        url: >-
                          https://example.com/downloads/effect.flac?expires=1790847972&signature=9f3c2a7e5b1d4c8a
                        expires_at: '2026-10-01T09:46:12Z'
                        duration: 5
                      error: null
                Failed:
                  value:
                    fx:
                      id: op_01m3vcs7e0f8rsq32d5sqhmetw
                      status: failed
                      created_at: '2026-10-01T09:30:00Z'
                      idempotency_key: 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
                      prompt: someone typing on a keyboard
                      duration: 5
                      output: null
                      error:
                        code: tos_violation
                        message: The request was rejected by our content policy.
        '401':
          description: The API key is missing or isn't valid.
          content:
            application/json:
              example:
                detail: not_authenticated
              schema:
                $ref: '#/components/schemas/NotAuthenticated'
        '402':
          description: >-
            The account doesn't have enough credits for this request. Nothing
            was submitted.
          content:
            application/json:
              example:
                detail:
                  type: insufficient_credits
                  msg: You have insufficient credits to perform this action.
              schema:
                $ref: '#/components/schemas/InsufficientCredits'
        '403':
          description: >-
            The API key is valid, but this account doesn't currently have API
            access: for example, the plan that included it has ended. A
            correctly set-up account never gets this, so don't retry: contact
            support.
          content:
            application/json:
              example:
                detail: not_authorized
              schema:
                $ref: '#/components/schemas/NotAuthorized'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
              example:
                detail:
                  - loc:
                      - body
                      - idempotency_key
                    msg: Field required
                    type: missing
        '429':
          description: >-
            Too many requests. Retry after the number of seconds in
            `Retry-After`.
          headers:
            Retry-After:
              description: How many seconds to wait before retrying.
              schema:
                type: integer
          content:
            application/json:
              example:
                detail: Rate limit exceeded; retry in 17 seconds
              schema:
                $ref: '#/components/schemas/RateLimited'
      x-codeSamples:
        - lang: bash
          source: >-
            # A complete program that also waits for the result and downloads
            it:

            # https://docs.soundpiece.co/guides/create/new_fx


            # One key per job: keep it, and send the same key if you retry.

            IDEMPOTENCY_KEY=$(uuidgen)


            curl --request PUT \
              --url https://api.soundpiece.co/v1/create.new_fx \
              --header 'Authorization: Bearer <token>' \
              --header 'Content-Type: application/json' \
              --data @- <<EOF
            {
              "idempotency_key": "$IDEMPOTENCY_KEY",
              "prompt": "someone typing on a keyboard",
              "duration": 5.0
            }

            EOF
        - lang: python
          source: >-
            # A complete program that also waits for the result and downloads
            it:

            # https://docs.soundpiece.co/guides/create/new_fx


            import uuid


            import requests


            url = "https://api.soundpiece.co/v1/create.new_fx"


            # One key per job: keep it, and send the same key if you retry.

            idempotency_key = str(uuid.uuid4())


            payload = {
                "idempotency_key": idempotency_key,
                "prompt": "someone typing on a keyboard",
                "duration": 5.0,
            }

            headers = {"Authorization": "Bearer <token>"}


            response = requests.put(url, json=payload, headers=headers,
            timeout=30)


            print(response.json())
        - lang: javascript
          source: >-
            // A complete program that also waits for the result and downloads
            it:

            // https://docs.soundpiece.co/guides/create/new_fx


            import { randomUUID } from "node:crypto";


            // One key per job: keep it, and send the same key if you retry.

            const idempotencyKey = randomUUID();


            const response = await
            fetch("https://api.soundpiece.co/v1/create.new_fx", {
              method: "PUT",
              headers: {Authorization: "Bearer <token>", "Content-Type": "application/json"},
              body: JSON.stringify({
                idempotency_key: idempotencyKey,
                prompt: "someone typing on a keyboard",
                duration: 5.0,
              }),
            });


            console.log(await response.json());
components:
  schemas:
    CreateNewFxRequest:
      properties:
        idempotency_key:
          type: string
          format: uuid
          title: Idempotency Key
          description: >-
            A unique UUID you choose. Send the same key to retry safely; we'll
            return the original response without doing the work again.
          examples:
            - 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
        callback_url:
          anyOf:
            - type: string
              maxLength: 2048
              minLength: 1
              format: uri
            - type: 'null'
          title: Callback Url
          description: >-
            Optional HTTPS URL we'll POST the final response to when the
            operation reaches `ready` or `failed`. Polling remains the canonical
            source of truth; webhooks are a best-effort latency optimisation.
          examples:
            - https://example.com/webhooks/soundpiece
        prompt:
          type: string
          maxLength: 450
          minLength: 1
          title: Prompt
          description: >-
            Free-text description of the sound effect to generate, up to 450
            characters. Describe the sound itself and its character — the
            source, the action and the setting. Examples: `someone typing on a
            keyboard`, `a deep cinematic impact with a long metallic tail`,
            `rising whoosh transition`.
        duration:
          type: number
          title: Duration
          description: Duration in seconds.
      additionalProperties: false
      type: object
      required:
        - idempotency_key
        - prompt
        - duration
      title: CreateNewFxRequest
    CreateNewFxResponse:
      properties:
        fx:
          $ref: '#/components/schemas/Fx'
      type: object
      required:
        - fx
      title: CreateNewFxResponse
    NotAuthenticated:
      properties:
        detail:
          type: string
          const: not_authenticated
          title: Detail
          description: >-
            Always `not_authenticated`, whatever the reason, so a key can't be
            probed. Check the `Authorization: Bearer <your-api-key>` header.
      type: object
      required:
        - detail
      title: NotAuthenticated
      description: The API key is missing, malformed, revoked or unknown.
    InsufficientCredits:
      properties:
        detail:
          $ref: '#/components/schemas/InsufficientCreditsDetail'
      type: object
      required:
        - detail
      title: InsufficientCredits
      description: |-
        The account doesn't have enough credits for this request. Nothing was
        submitted and nothing was charged: top up, then retry with the same
        `idempotency_key`.
    NotAuthorized:
      properties:
        detail:
          type: string
          const: not_authorized
          title: Detail
          description: 'Always `not_authorized`. Retrying won''t help: contact support.'
      type: object
      required:
        - detail
      title: NotAuthorized
      description: |-
        The API key is valid, but its account doesn't currently have API access.
        A correctly set-up account never gets this.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    RateLimited:
      properties:
        detail:
          type: string
          title: Detail
          description: Human-readable message.
      type: object
      required:
        - detail
      title: RateLimited
      description: |-
        Too many requests in a short time. Wait for the number of seconds in the
        `Retry-After` header, then retry; a `PUT` retried with the same
        `idempotency_key` never creates the work twice.
    Fx:
      properties:
        id:
          type: string
          pattern: ^op_[0-9a-z]{26}$
          title: Id
          description: >-
            A unique identifier for an operation, prefixed `op_`. Returned from
            any `PUT` endpoint; pass it to the matching `GET` to poll, or use it
            to correlate webhook deliveries with your records.
          examples:
            - op_01m3vcs7e0f8rsq32d5sqhmetw
        status:
          type: string
          enum:
            - processing
            - ready
            - failed
          title: Status
          description: >-
            Current state of the operation. `processing` means work is in
            flight; `ready` means the result is available (the
            `output`/`outputs` field is populated); `failed` means something
            went wrong (the `error` field is populated).
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When this operation was created (ISO-8601, UTC).
        idempotency_key:
          type: string
          format: uuid
          title: Idempotency Key
          description: >-
            A unique UUID you choose. Send the same key to retry safely; we'll
            return the original response without doing the work again.
          examples:
            - 3f2b8c1e-9d4a-4e7b-b6c5-1a2d3e4f5a6b
        prompt:
          type: string
          maxLength: 1024
          minLength: 1
          title: Prompt
          description: >-
            Free-text description of the audio to generate. Mix tags, a
            description of the desired mood and instrumentation, and a scene —
            use commas to separate tags. Examples: `rock song, energetic, male
            vocal` (song), `synthwave, melancholic, mid-tempo` (instrumental),
            `rock, guitar, energetic, no drums` (sample), `someone typing on a
            keyboard` (effect).
        duration:
          type: number
          title: Duration
          description: Duration in seconds.
        output:
          anyOf:
            - $ref: '#/components/schemas/AudioOutput'
            - type: 'null'
          description: Populated when `status` is `ready`.
        error:
          anyOf:
            - $ref: '#/components/schemas/PromptError'
            - type: 'null'
          description: >-
            Populated when `status` is `failed`. The `code` is a stable
            machine-readable identifier; `message` is a human-readable
            description.
      type: object
      required:
        - id
        - status
        - created_at
        - idempotency_key
        - prompt
        - duration
      title: Fx
    InsufficientCreditsDetail:
      properties:
        type:
          type: string
          const: insufficient_credits
          title: Type
          description: 'Stable machine-readable code: safe to branch on.'
        msg:
          type: string
          title: Msg
          description: Human-readable message.
      type: object
      required:
        - type
        - msg
      title: InsufficientCreditsDetail
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    AudioOutput:
      properties:
        url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Url
          description: >-
            A signed URL to download the audio as a FLAC file (44.1 kHz).
            Time-limited; see `expires_at`. Don't cache the URL itself.
        expires_at:
          type: string
          format: date-time
          title: Expires At
          description: >-
            When the URL stops working (ISO-8601, UTC). Call the matching `get`
            endpoint again to receive a fresh URL.
        duration:
          type: number
          title: Duration
          description: Duration in seconds.
        loudness:
          $ref: '#/components/schemas/OutputLoudness'
          title: Loudness
          description: >-
            Present only when loudness normalisation was requested: what was
            measured on this output. Measured rather than assumed, so it can
            differ from the target when the audio couldn't reach it (see
            `allow_compression`).
      type: object
      required:
        - url
        - expires_at
        - duration
      title: AudioOutput
    PromptError:
      properties:
        code:
          anyOf:
            - type: string
              enum:
                - internal_error
                - generation_timeout
            - type: string
              const: tos_violation
          title: Code
          description: >-
            Stable machine-readable error code — safe to branch on. This
            endpoint can only return the codes listed here.
        message:
          type: string
          title: Message
          description: Human-readable error message.
      type: object
      required:
        - code
        - message
      title: PromptError
      description: |-
        Error for prompt-only generation endpoints (`new_sample`, `new_fx`) — no
        source URL, so none of the `source_*` codes apply.
    OutputLoudness:
      properties:
        lufs:
          type: number
          title: Lufs
          description: Integrated loudness measured on the delivered audio, in LUFS.
        true_peak_dbtp:
          type: number
          title: True Peak Dbtp
          description: True peak measured on the delivered audio, in dBTP.
        compressed:
          type: boolean
          title: Compressed
          description: >-
            Whether reaching the target meant altering the audio's dynamics.
            `false` means only its level changed.
      type: object
      required:
        - lufs
        - true_peak_dbtp
        - compressed
      title: OutputLoudness
      description: What was measured on a normalised output.
  securitySchemes:
    Bearer:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.