{
  "openapi": "3.1.0",
  "info": {
    "title": "Mirelo API",
    "version": "3.0.0",
    "description": "Generate sound for video. Describe what you want to hear and get back audio you can drop onto a timeline.\n\n## Base URL and authentication\n\n```\nAuthorization: Bearer sk-...\n```\n\n## What is available today\n\n`text-to-sfx`, `video-to-sfx`, `extend` and `inpaint` are served under `/v3`. `GET /v3/models` is the authority on what exists: a model's `operations` lists the endpoint families it accepts, and a family that is absent is not served. Read limits from there rather than hardcoding them, because the request schemas deliberately do not pin the ceilings.\n\nAudio-to-MIDI Pro has no v3 collection yet. Its published paths stay `/v2/audio-to-midi/v1.0/…` and are included in this document so a generated client can transcribe without switching specs. The v1.0 path versions the request and response contract; completed results identify the model release separately.\n\n## Versioning\n\nAPI versions and model releases are separate:\n\n- `Mirelo-Version` selects the shape and meaning of the HTTP/JSON contract.\n- `model` selects a public model release, such as `sfx-1.6`.\n\nEvery v3 create and preflight requires an explicit model id. There is no default and no floating `latest` alias.\n\nRetain a job's `model` with its output. It identifies the model release used for that job.\n\nThere is one version today, `2026-08-28`, and every response carries it. You may send it too, and a date we accept comes back on the response, so a log says which contract served you. A date this API never had is rejected rather than ignored, and that rejection carries the current date rather than the one you sent - read the header as what served you, not as an echo of your request. What sending it does *not* do yet is select an older shape - there is no second behaviour to serve.\n\nWhat it does buy you is a rule about how this API changes. These ship without a new date: a new field on a response, a new optional field on a request, a new endpoint, and a new value in one of the lists this document marks open - a job's `status`, an output's `type` and `category`, and `error.code`, whose descriptions each tell you to handle a value you do not recognise. Do that, and none of them reaches you as a break.\n\nRead those three qualifiers strictly, because the same field name is closed elsewhere: a job's `status` is open, an output's own `status` is not, and `type` is open on an output while it is closed on the media you send in.\n\nEverything else mints a new date. A change to what an existing field *means* - a different unit, a narrower range, a field withdrawn - and also a new value in any of the enums here that is *not* marked open, since a generated client refuses one of those rather than passing it through.\n\nHow long a date is honoured once a second one exists, and how one is retired, is deliberately unsettled: there is nothing to retire while there is one. Those rules will be published alongside the second version.\n\n## Waiting for a result\n\nA create returns `202` and a job you poll. Add `?wait=25` (or `Prefer: wait=25`) to have the server hold the request for up to 25 seconds: you get `200` with the finished job if it lands inside the window, `202` if it does not. The body is the same object either way.\n\n`GET .../{id}` answers `200` whatever state the job is in, `?wait=` included. A long-poll that runs out of time is still `200`, with a `status` that is not final and a `Retry-After` for the next attempt. Branch on `status`, never on the HTTP status.\n\n## Repeating a request safely\n\nSend an `Idempotency-Key` on anything that costs credits. The same key with the same body returns the original job rather than charging twice, and carries `Idempotent-Replayed: true`. It is also how you recover a job id if the connection drops while the server is holding your request. Same key with a *different* body is `409`.\n\n## Errors\n\nBranch on `code`, never on `message`. Use `retryable` rather than inferring from the status code: an expired download URL is a `400` worth retrying, and an insufficient balance is a `402` that never is. `param` names the offending field where one field is to blame.\n\nTreat the code list as open, and handle one you do not recognise rather than failing.\n\nA `402` also carries `error.credit_recovery` whenever the refusal came from checking the ledger, which today is every one of them: how many credits the request needed, what the account can actually spend, and the one billing step that fixes it. Read it defensively rather than assuming it.\n\nYou never have to spend a failed request to get that answer. Preflight returns the same object for the same body, so ask it there first. `GET /v3/me` answers the coarser version - what the account can spend at all, and the step if it can spend nothing.\n\n## Moving from v2\n\nThree things v2 accepted are refused here, each with `invalid_request` rather than the older behaviour:\n\n- **`seed: -1`.** v2 read it as \"pick one for me\". Here that is what leaving `seed` out does, and the seed that was used comes back on the job. A negative seed is rejected rather than reinterpreted, so a request that meant to pin one cannot silently become a random one.\n- **A fractional `duration_ms`.** v2 rounded it; v3 takes whole milliseconds and refuses the rest, so what you asked for and what you are billed for are the same number.\n- **`content_type` on `POST /v3/assets`.** Required, and a plain `audio/*` or `video/*` type with no `charset` or `codecs` clause. v2 let it be absent and stored whatever the upload sent, which surfaced much later as a format rejection at generation time; here it is signed into the upload policy.\n\nOne difference to plan around rather than fix in your request: v2's upload URLs carried no size limit at all, and `max_bytes` on a v3 ticket is a condition of the upload policy that storage itself enforces. It is sized off the longest window a model publishes for a file it reads, so a window you can ask for is a window you can upload for. Two shapes need more file than that: a window taken from further in, because `start_offset_ms` is skipped before it begins, and the edit endpoints, which bound what they generate and put no bound at all on the source it is added to. Both are legitimate requests. For those, and for any other file over the limit, serve it yourself and pass `{ \"type\": \"url\" }` wherever a request takes media: a URL has no size limit, because the bytes are never ours.\n\n## Rate limits\n\n`RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` / `RateLimit-Policy` come back on a response whose key authenticated, alongside `Mirelo-Concurrency-Limit`. Read all four as optional: if the limiter itself is unreachable we serve your request and leave them off rather than publish a number we did not read.\n\nThere are two numbers because a bucket has two, and they are not the same size. `RateLimit-Limit` is a **burst**: the most you may spend at once, and what `RateLimit-Remaining` counts down from. `RateLimit-Policy` is the **sustained rate** it refills at - `\"v3-read\";q=600;w=60` reads as `q` requests per `w` seconds. Size a steady workload off `q`/`w`; use `RateLimit-Limit` only for how much you may spend in one go. Sizing steady traffic off the burst promises headroom you can reach once and then not again for the rest of the window.\n\n`RateLimit-Reset` is **seconds from now** until the burst is full again, `0` when it already is, and never a timestamp. It is not a window boundary: refilling is continuous, so you can spend again well before it reaches `0`.\n\nThe name in `RateLimit-Policy` is the pool the request was counted against. Reads, writes and generations have separate pools, so polling a job does not spend what creating one does.\n\n`Mirelo-Concurrency-Current` comes back from the endpoints that start or track a generation, since counting your in-flight jobs is work an account lookup should not pay for. For generation the concurrency pair is the ceiling you hit first. The ceiling is set per key: by default at most 5 of your jobs run at once, and `Mirelo-Concurrency-Limit` carries the number for your key.",
    "contact": {
      "name": "Mirelo support",
      "url": "https://mirelo.ai"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://mirelo.ai/terms"
    },
    "summary": "Generate sound for video"
  },
  "servers": [
    {
      "url": "https://api.mirelo.ai"
    }
  ],
  "tags": [
    {
      "name": "Generate",
      "description": "Making new sound."
    },
    {
      "name": "Edit",
      "description": "Changing sound you already have."
    },
    {
      "name": "Audio-to-MIDI Pro",
      "description": "Transcribe audio into MIDI, structured notes, and MusicXML. These paths are still /v2/audio-to-midi/… — there is no v3 collection yet, and the bodies are the v2 shapes."
    },
    {
      "name": "Models",
      "description": "What each model can do."
    },
    {
      "name": "Assets",
      "description": "Getting a local file to us."
    },
    {
      "name": "Account",
      "description": "Who this key is, and what is left to spend."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key, sent as `Authorization: Bearer sk-...`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "The body of every 4xx and 5xx.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "invalid_region",
                  "capability_unsupported",
                  "model_not_found",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "no_active_subscription",
                  "insufficient_credits",
                  "idempotency_conflict",
                  "payload_too_large",
                  "invalid_video",
                  "video_url_unreachable",
                  "video_format_unsupported",
                  "video_too_short",
                  "invalid_audio",
                  "audio_url_unreachable",
                  "audio_format_unsupported",
                  "invalid_asset",
                  "asset_not_ready",
                  "audio_too_short",
                  "audio_too_long",
                  "moderation_blocked",
                  "rate_limited",
                  "concurrency_limit_reached",
                  "upstream_rate_limited",
                  "generation_failed",
                  "generation_timeout",
                  "result_unreadable",
                  "temporarily_unavailable",
                  "server_error"
                ],
                "x-speakeasy-unknown-values": "allow",
                "description": "Machine-readable. Branch on this, not on the message. Treat this list as open: handle a code you do not recognise rather than failing."
              },
              "message": {
                "type": "string",
                "description": "Human-readable, and free to change."
              },
              "param": {
                "type": "string",
                "description": "Which field caused it, when one field did."
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether the same request might succeed later. Use this rather than inferring from the status code."
              },
              "request_id": {
                "type": "string",
                "description": "Quote this when you contact us."
              }
            }
          }
        }
      },
      "PaymentRequiredError": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "The body of a 402.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "invalid_region",
                  "capability_unsupported",
                  "model_not_found",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "no_active_subscription",
                  "insufficient_credits",
                  "idempotency_conflict",
                  "payload_too_large",
                  "invalid_video",
                  "video_url_unreachable",
                  "video_format_unsupported",
                  "video_too_short",
                  "invalid_audio",
                  "audio_url_unreachable",
                  "audio_format_unsupported",
                  "invalid_asset",
                  "asset_not_ready",
                  "audio_too_short",
                  "audio_too_long",
                  "moderation_blocked",
                  "rate_limited",
                  "concurrency_limit_reached",
                  "upstream_rate_limited",
                  "generation_failed",
                  "generation_timeout",
                  "result_unreadable",
                  "temporarily_unavailable",
                  "server_error"
                ],
                "x-speakeasy-unknown-values": "allow",
                "description": "Machine-readable. Branch on this, not on the message. Treat this list as open: handle a code you do not recognise rather than failing."
              },
              "message": {
                "type": "string",
                "description": "Human-readable, and free to change."
              },
              "param": {
                "type": "string",
                "description": "Which field caused it, when one field did."
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether the same request might succeed later. Use this rather than inferring from the status code."
              },
              "request_id": {
                "type": "string",
                "description": "Quote this when you contact us."
              },
              "credit_recovery": {
                "$ref": "#/components/schemas/CreditRecovery"
              }
            }
          }
        }
      },
      "CreditRecovery": {
        "type": "object",
        "properties": {
          "credits_required": {
            "type": "integer",
            "description": "What the request costs. On a 402 that is what it would have cost had it run; on a quote it is the same figure as `credits`."
          },
          "credits_available": {
            "type": "integer",
            "description": "The ledger balance, less credits already committed by jobs in flight, plus any enabled overage headroom. Provisioning can still block spending this amount: check `provisioning_state`. Once provisioning is ready, this is `spend_capacity` from `GET /v3/me`, not the ledger balance that endpoint calls `credits_available`."
          },
          "credit_shortfall": {
            "type": "integer",
            "minimum": 0,
            "description": "`credits_required` minus `credits_available`, floored at zero. Zero on a refusal means provisioning blocked the request, not the balance."
          },
          "recovery_action": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "enable_overage",
              "increase_overage_limit",
              "view_plans",
              "contact_support",
              null
            ],
            "description": "The one step that fixes this, or null when there is nothing for the account holder to do yet - a grant still provisioning is the usual reason."
          },
          "recovery_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Where to do it. Null exactly when `recovery_action` is."
          },
          "provisioning_state": {
            "type": "string",
            "enum": [
              "ready",
              "pending",
              "stalled"
            ],
            "description": "Whether this period's included credits have landed. Anything but `ready` blocks spending however healthy the balance looks."
          },
          "provisioning_deadline": {
            "type": [
              "integer",
              "null"
            ],
            "description": "ms epoch when a `pending` grant becomes `stalled`. Wait for it rather than looping, and null when provisioning does not apply."
          }
        },
        "required": [
          "credits_required",
          "credits_available",
          "credit_shortfall",
          "recovery_action",
          "recovery_url",
          "provisioning_state",
          "provisioning_deadline"
        ],
        "description": "How short the account is, and where to fix it."
      },
      "AudioToMidiRequest": {
        "type": "object",
        "properties": {
          "audio": {
            "$ref": "#/components/schemas/AudioSource"
          },
          "timing": {
            "type": "string",
            "enum": [
              "performance",
              "quantized"
            ],
            "default": "performance",
            "description": "How note times are written. `performance` (default) keeps the take as played and describes it with a tempo map. `quantized` snaps notes onto the detected beats, and is honoured only when the beat grid is reliable enough to move notes onto — otherwise the response reports `performance` with a `fallback_reason`."
          },
          "subdivision": {
            "type": "string",
            "enum": [
              "automatic",
              "straight_sixteenths",
              "eighth_triplets",
              "sixteenth_triplets",
              "swing_eighths"
            ],
            "description": "Rhythmic grid shared by MIDI and MusicXML. Automatic selects triplet timing only when onset fit clears the confidence gate; sparse or ambiguous material stays on straight sixteenths."
          },
          "time_signature": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "numerator": {
                    "anyOf": [
                      {
                        "type": "number",
                        "enum": [
                          2
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          3
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          4
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          5
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          6
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          7
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          9
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          12
                        ]
                      }
                    ]
                  },
                  "denominator": {
                    "type": "number",
                    "enum": [
                      4
                    ]
                  }
                },
                "required": [
                  "numerator",
                  "denominator"
                ],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "numerator": {
                    "anyOf": [
                      {
                        "type": "number",
                        "enum": [
                          6
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          9
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          12
                        ]
                      }
                    ]
                  },
                  "denominator": {
                    "type": "number",
                    "enum": [
                      8
                    ]
                  }
                },
                "required": [
                  "numerator",
                  "denominator"
                ],
                "additionalProperties": false
              }
            ],
            "description": "Governing score meter. Omit it to retain the detected pulse grouping as N/4. Beat timestamps and triplet onsets alone cannot distinguish simple from compound meter; select 6/8, 9/8, or 12/8 explicitly. Existing N/4 choices are never reinterpreted as /8.",
            "example": {
              "numerator": 6,
              "denominator": 8
            }
          },
          "tempo": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "fixed"
                ],
                "description": "Write one constant tempo instead of following the take's pacing."
              },
              "bpm": {
                "type": "number",
                "minimum": 30,
                "maximum": 300,
                "description": "Constant tempo to write. Omit it to use Mirelo's detected BPM as one constant tempo.",
                "example": 120
              }
            },
            "required": [
              "mode"
            ],
            "additionalProperties": false,
            "description": "Optional fixed-tempo output. Omit `tempo` to preserve the take's pacing. With `mode: fixed`, omit `bpm` to flatten the take at Mirelo's detected BPM, or provide a BPM from 30 through 300."
          },
          "optimize_musicxml": {
            "type": "boolean",
            "default": false,
            "description": "Request more conventional, readable sheet music, with improved staff assignment, voices, beaming, rests, and spacing. This affects only the MusicXML output; it does not improve transcription accuracy or change MIDI or note data. This option adds processing time and is disabled by default. Synchronous requests wait for the score, and asynchronous jobs remain processing until it is ready. The result's `musicxml_optimized` reports whether the optimized score was delivered; when it could not be, the standard MusicXML is returned instead.",
            "example": true
          },
          "page_size": {
            "type": "string",
            "enum": [
              "a4",
              "letter"
            ],
            "default": "a4",
            "description": "Paper layout used by MusicXML and sheet music PDFs: `a4` (210 x 297 mm) or `letter` (8.5 x 11 in). Defaults to `a4`.",
            "example": "letter"
          },
          "score_pdfs": {
            "type": "boolean",
            "default": false,
            "description": "Also deliver engraved sheet music as PDFs: the full score, one PDF per part, tablature for eligible fretted parts, and one ZIP containing those PDFs. Implies `optimize_musicxml`, since the PDFs are set from the MusicXML you receive. Adds processing time and is disabled by default. When PDF rendering is unavailable the response omits `score_bundle_url`, `score_manifest_url`, and `score_pdfs` and the MIDI and MusicXML are still returned.",
            "example": true
          },
          "instruments": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "acoustic_piano",
                "electric_piano",
                "chromatic_percussion",
                "organ",
                "acoustic_guitar",
                "clean_electric_guitar",
                "distorted_electric_guitar",
                "acoustic_bass",
                "electric_bass",
                "violin",
                "viola",
                "cello",
                "contrabass",
                "orchestral_harp",
                "timpani",
                "string_ensemble",
                "synth_strings",
                "voice",
                "orchestra_hit",
                "trumpet",
                "trombone",
                "tuba",
                "french_horn",
                "brass_section",
                "soprano_and_alto_sax",
                "tenor_sax",
                "baritone_sax",
                "oboe",
                "english_horn",
                "bassoon",
                "clarinet",
                "flutes",
                "synth_lead",
                "synth_pad",
                "drums"
              ],
              "example": "acoustic_guitar"
            },
            "maxItems": 35,
            "description": "Every instrument in the recording. Optional: without it the model transcribes whatever it hears. A list is treated as exhaustive. An instrument missing from it cannot appear in the output, and a name that is not playing makes the model split a real part in two, so a partial list is worse than none. Order does not matter. An unknown name is a 400. Accepted names: acoustic_piano, electric_piano, chromatic_percussion, organ, acoustic_guitar, clean_electric_guitar, distorted_electric_guitar, acoustic_bass, electric_bass, violin, viola, cello, contrabass, orchestral_harp, timpani, string_ensemble, synth_strings, voice, orchestra_hit, trumpet, trombone, tuba, french_horn, brass_section, soprano_and_alto_sax, tenor_sax, baritone_sax, oboe, english_horn, bassoon, clarinet, flutes, synth_lead, synth_pad, drums.",
            "example": [
              "acoustic_guitar",
              "voice"
            ]
          },
          "instrument_detection_id": {
            "type": "string",
            "minLength": 1,
            "description": "The `instrument_detection_id` from POST /v2/audio-to-midi/v1.0/instruments whose suggestions you reviewed. It must be yours, and `audio` must be an asset with the same content (the detection's `asset_id` works). When the transcription succeeds the detection counts as used, and if the detection was charged, its credits come off this transcription's charge.",
            "example": "k57b1x2m8f3nq9d0c4v6s1h7j8a2p3e5"
          }
        },
        "required": [
          "audio"
        ],
        "additionalProperties": false,
        "example": {
          "audio": {
            "type": "url",
            "audio_url": "https://example.com/song.mp3"
          },
          "timing": "quantized",
          "subdivision": "automatic",
          "time_signature": {
            "numerator": 6,
            "denominator": 8
          },
          "tempo": {
            "mode": "fixed",
            "bpm": 120
          },
          "optimize_musicxml": true,
          "instruments": [
            "acoustic_guitar",
            "voice"
          ]
        }
      },
      "AudioSource": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "url"
                ]
              },
              "audio_url": {
                "type": "string",
                "format": "uri",
                "description": "Publicly accessible audio URL (no auth required), served with an audio Content-Type (audio/* or application/ogg). Supported formats: WAV, FLAC, MP3, OGG, AAC. An audio URL check is performed before generation."
              }
            },
            "required": [
              "type",
              "audio_url"
            ],
            "title": "URL"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "asset"
                ]
              },
              "asset_id": {
                "type": "string",
                "format": "uuid",
                "description": "Customer asset ID obtained from POST /v2/assets"
              }
            },
            "required": [
              "type",
              "asset_id"
            ],
            "title": "Asset"
          }
        ]
      },
      "AudioToMidiInstrumentsRequest": {
        "type": "object",
        "properties": {
          "audio": {
            "$ref": "#/components/schemas/AudioSource"
          },
          "max_credits": {
            "type": "integer",
            "minimum": 0,
            "description": "The most this request may charge. With `0` the request runs only when it is free and otherwise fails with `max_credits_exceeded`, charging nothing. Omit it to allow the price.",
            "example": 0
          }
        },
        "required": [
          "audio"
        ],
        "additionalProperties": false,
        "example": {
          "audio": {
            "type": "asset",
            "asset_id": "3f1f4c1e-8a4b-4b8e-9f9e-2b6c1d0a7e55"
          },
          "max_credits": 0
        }
      },
      "TextToSfxJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "partially_succeeded",
              "failed",
              "canceled",
              "expired"
            ],
            "description": "Where the job is. Keep polling until it is final: `succeeded`, `partially_succeeded`, `failed`, `canceled` or `expired`.\n\nTreat this list as open and handle a state you do not recognise rather than failing - a new one is added when the pipeline grows a stage, not when the contract changes.",
            "x-speakeasy-unknown-values": "allow"
          },
          "model": {
            "type": "string",
            "description": "The public model release used for this job."
          },
          "seed": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the job reached a final state. Null while it is still `queued` or `running`, and null on an `expired` job that never reached one - expiry is the clock running out rather than an outcome, so there is nothing to report and nothing is invented. A fabricated one moved every time the same job was polled."
          },
          "expires_at": {
            "type": "string"
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Roughly how far along, as a fraction.\n\nDerived from elapsed time against `estimated_ms` rather than reported by the worker, so it moves smoothly and knows nothing about what inference is actually doing. It climbs towards 1 without reaching it, including for a job that outlives its estimate, so it never claims to be done - `status` is what says that.\n\nPresent while `queued` or `running` and absent once the job is final, so a finished job carries no number that reads as almost."
          },
          "estimated_ms": {
            "type": "integer",
            "description": "How long this job is expected to take, measured from `created_at`. Size your own timeout from this.\n\n`Retry-After` answers a different question - how long to wait before the **next** poll - and is capped at 30 seconds, so a job that will take three minutes says 30 there. This is the whole expected run.\n\nAn estimate from how long this shape of work has taken before, not a commitment: a job may finish well inside it or run well past it. Present while `queued` or `running` and dropped once the job is final, the same way `progress` is, and absent for the moment between a create being accepted and its row landing - there is nothing to estimate from yet."
          },
          "credits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What was actually debited, which is not always what the outputs suggest.\n\nBilling is all-or-nothing on the variant count you **requested**: a job that asked for three and delivered one is still debited for three, so a `partially_succeeded` job reports the full charge rather than a share of it. Read each output's `status` and each variant's to see what you got; do not infer it from this number.\n\nA `failed` job can also report a charge, and one case is not a mistake: `result_unreadable` means the audio was generated - and debited - but could not be read back, so it cannot be handed over. This field reports what the ledger took rather than zero, because the debit is real; the error's `retryable: false` is what tells you not to pay for it twice.\n\n`null` while the job is not finished, and on an organization key, which is logged rather than debited. `0` when no debit was recorded at all - including an `expired` job that never succeeded."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobError"
            }
          },
          "urls": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              }
            },
            "required": [
              "self"
            ]
          },
          "object": {
            "type": "string",
            "enum": [
              "text_to_sfx.generation"
            ]
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "output": {
                "$ref": "#/components/schemas/Output"
              }
            },
            "required": [
              "output"
            ]
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "created_at",
          "completed_at",
          "expires_at",
          "credits",
          "errors",
          "urls",
          "object",
          "result"
        ],
        "description": "A text-to-sfx job."
      },
      "JobError": {
        "type": "object",
        "properties": {
          "output_index": {
            "type": [
              "integer",
              "null"
            ]
          },
          "variant_index": {
            "type": [
              "integer",
              "null"
            ]
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether sending the same request again is worth trying.\n\nPublished rather than left to be read off a status code, which is the v2 spelling and cannot work here: this error arrives inside a `200` poll, so it has no status of its own, and a number describing what the create would have answered would be one we invented. Where a status does exist it gets this wrong in both directions anyway - a `400` for an expired presigned URL is worth retrying, and a `402` never is however many times you try."
          }
        },
        "required": [
          "output_index",
          "variant_index",
          "code",
          "message",
          "retryable"
        ],
        "description": "Something that went wrong inside a job."
      },
      "Output": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "mix",
              "speech",
              "music",
              "sfx",
              "ambience"
            ],
            "description": "What kind of audio this is. Treat this list as open: handle a kind you do not recognise rather than failing.",
            "x-speakeasy-unknown-values": "allow"
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "A specific name, when this output is one detected sound rather than a general kind of audio. Null otherwise."
          },
          "start_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where this clip belongs on the timeline, in milliseconds from the start of the source video, when the model placed it. For a separated sound this is the generated window, which reaches past the sound itself; see `sound_start_ms`."
          },
          "end_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where this clip ends on the timeline. See `start_ms`."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "foreground",
              "background",
              "swoosh",
              null
            ],
            "description": "The role a separated sound plays: `foreground` is a sound the on-screen action makes, `background` is ambience under the scene, and `swoosh` is a short sound that accompanies fast motion. Treat this list as open and handle a value you do not recognise. Null when the output is not a separated sound, and on a plan saved before this field existed.",
            "x-speakeasy-unknown-values": "allow"
          },
          "sound_start_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where the sound itself begins on the source video, in milliseconds. `start_ms` and `end_ms` are the generated window, which reaches past the sound to give each end some lead-in and tail audio, so trim the clip to this span and use the difference to crossfade. Null when the output is not a separated sound, and on a plan saved before this field existed."
          },
          "sound_end_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where the sound itself ends on the source video, in milliseconds. See `sound_start_ms`. Null in the same cases."
          },
          "credits": {
            "type": "integer",
            "description": "This output's share of the debit, on the same requested-count basis as the job's `credits` - not a per-delivered-variant price."
          },
          "variants_requested": {
            "type": "integer",
            "description": "How many alternatives were attempted for this output.\n\nOn a finished job, do not compare it with the length of `variants` to detect a partial failure. A shortfall takes two shapes and that comparison only sees one of them: a variant the model never produced is absent from `variants`, but one it produced whose file could not be described is present with `status: \"failed\"` and `files: null`, which leaves the lengths equal. Read this output's `status` for whether anything is missing, and each variant's `status` before reading its `files`."
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "partially_succeeded",
              "failed"
            ],
            "description": "Whether every requested alternative came back. This is the partial-failure check: it covers both shapes a shortfall takes, and the job's `errors` names the `variant_index` of each one either way."
          },
          "gain_db": {
            "type": [
              "number",
              "null"
            ]
          },
          "generation_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The stored generation behind this output, to attach feedback to it later.\n\nPer output rather than per job, because a request that separates a clip persists one generation per part and a single id could not name them - which is why the v2 spelling of this field answers nothing for a separation job.\n\nNull when nothing was persisted for this output. Keeping a copy is best-effort, and some outputs are never kept at all: the isolated speech a preserve-speech job returns is your own audio rather than anything we generated. It is not on `PlannedOutput` for the same reason `variants` is not - the copy is made after the audio exists."
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            }
          }
        },
        "required": [
          "index",
          "type",
          "label",
          "start_ms",
          "end_ms",
          "category",
          "sound_start_ms",
          "sound_end_ms",
          "credits",
          "variants_requested",
          "status",
          "gain_db",
          "generation_id",
          "variants"
        ]
      },
      "Variant": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed"
            ],
            "description": "Whether this alternative can be handed back. Check it before reading `files`: a variant the model produced but whose file could not be described is present here as `failed`, so an entry existing is not the same as an entry you can use."
          },
          "files": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "audio": {
                "$ref": "#/components/schemas/AudioFile"
              }
            },
            "description": "Null when `status` is `failed`. A failed variant has no files rather than empty ones, so a missing result is one check instead of two."
          }
        },
        "required": [
          "index",
          "status",
          "files"
        ],
        "description": "One alternative for an output. Pick the one you like."
      },
      "AudioFile": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Download link, valid until `url_expires_at`. Fetch the job again for a fresh one."
          },
          "url_expires_at": {
            "type": "string",
            "description": "When this link stops working. Timed from the read, not from when the job finished: fetching the job again before its own `expires_at` issues fresh links."
          },
          "bytes": {
            "type": "integer"
          },
          "format": {
            "type": "string",
            "enum": [
              "wav",
              "flac",
              "mp3_128",
              "mp3_192",
              "mp3_256",
              "mp3_320",
              "m4a_128",
              "m4a_192",
              "m4a_256"
            ]
          },
          "sample_rate": {
            "type": "integer",
            "description": "This file's rate, which is not always the model's. Generated audio comes back at the model's native rate, published as `audio.sample_rate` on the model; a `speech` stem is separated out of your own audio rather than generated, so it can differ - read it from here rather than from the model."
          },
          "channels": {
            "type": "integer",
            "description": "This file's channel count, the same way `sample_rate` is its rate."
          },
          "duration_ms": {
            "type": "integer"
          }
        },
        "required": [
          "url",
          "url_expires_at",
          "bytes",
          "format",
          "sample_rate",
          "channels",
          "duration_ms"
        ],
        "description": "An audio file."
      },
      "TextToSfxRequest": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "The public model release to use. Required on every create and preflight, with no default or floating `latest` alias. `GET /v3/models` lists available releases and their supported operations.",
            "example": "sfx-1.6"
          },
          "duration_ms": {
            "type": "integer",
            "minimum": 100,
            "description": "How much audio to generate. The ceiling is per model, in `operations` on the model. It is not fixed here so that raising it for one model does not make a generated client reject a request the API accepts.",
            "example": 5000
          },
          "num_variants": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "How many variations to generate. The ceiling is per model, in `operations` on the model. Asking for more than it allows is rejected rather than silently capped."
          },
          "seed": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4294967295,
            "description": "Starting point for the randomness. Omit it and we pick one, and report it back on the job. A seed narrows the result, it does not pin it."
          },
          "input": {
            "$ref": "#/components/schemas/TextInput"
          },
          "controls": {
            "$ref": "#/components/schemas/LoopControls"
          },
          "output": {
            "$ref": "#/components/schemas/AudioOutputBlock"
          }
        },
        "required": [
          "model",
          "duration_ms",
          "input"
        ]
      },
      "TextInput": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "description": "What you want to hear. Describe the sound, not the picture. Too long a prompt is rejected rather than truncated; the ceiling is per model, in `max_prompt_chars`. Some models treat the prompt as experimental; see `operations.<family>.experimental`.",
            "example": "gravel footsteps, heavy and slow, in a stone corridor"
          },
          "negative_prompt": {
            "type": "string",
            "minLength": 1,
            "description": "Sounds you would rather not hear, as a short comma-separated list. Not every model takes one: where `max_negative_prompt_chars` on the model is 0, sending it is refused. Too long a negative prompt is rejected rather than truncated. Some models treat it as experimental; see `operations.<family>.experimental`.",
            "example": "music, speech, crowd noise"
          }
        },
        "required": [
          "prompt"
        ],
        "description": "What the model works from."
      },
      "LoopControls": {
        "type": "object",
        "properties": {
          "loop": {
            "type": "boolean",
            "default": false,
            "description": "Make the result loop cleanly end to end."
          }
        },
        "description": "Model-gated behaviour. Check `controls` on the model before relying on one."
      },
      "AudioOutputBlock": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "wav",
              "flac",
              "mp3_128",
              "mp3_192",
              "mp3_256",
              "mp3_320",
              "m4a_128",
              "m4a_192",
              "m4a_256"
            ],
            "default": "wav",
            "description": "How the audio comes back. Container and, for the lossy ones, encoding rate in kbps. `wav` and `flac` are lossless. `m4a` is AAC in an MP4 container. Sample rate is not selectable: generated audio comes back at the model's native rate, which is on the model as `audio.sample_rate`. Every file carries its own `sample_rate` and `channels`, which are not always the model's - the `speech` stem under `controls.preserve_speech` is separated out of your own audio rather than generated, so its rate is the separation's and today that is 48 kHz. Read the file's own fields before muxing or encoding."
          }
        },
        "description": "What comes back."
      },
      "Preflight": {
        "type": "object",
        "properties": {
          "credits": {
            "type": "integer",
            "description": "What this request costs if it succeeds.\n\nOn a metered key this is exactly what will be debited. An organization key is logged rather than debited, so for one of those this is the list price and the job's own `credits` comes back null."
          },
          "estimated_ms": {
            "type": "integer"
          },
          "plan_id": {
            "type": "string",
            "description": "Present only when `outputs` is. Pass it back as `plan` on the real request and the detection is not repeated: the same parts are generated, and you are billed from this quote rather than from a fresh pass.\n\nWithout it the real request has nothing to generate from - on this model separated parts come only from a plan, so a create that asks for them without one is refused rather than quietly detecting again and charging you for a set you were never shown.",
            "example": "plan_4Qb8vT2nHk"
          },
          "plan_expires_at": {
            "type": "string",
            "description": "When `plan_id` stops being accepted, 24 hours after this response. After that, preflight again for a new one."
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlannedOutput"
            },
            "description": "What the real request would produce, in the order it would come back. Present only when the request asks for separated stems, because that is the only case where the answer is not arithmetic.\n\nEach entry has the same fields as the `Output` it becomes, minus the audio, so you can render the plan with the code that renders the result."
          },
          "credit_recovery": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreditRecovery"
              },
              {
                "description": "Whether this request can be funded, and the one step to take if not - resolved against the `credits` above rather than against some minimum, so `recovery_action` answers whether you can afford **this** body and not merely whether you can afford anything.\n\nThe same object a 402 carries, so the answer you get here is the answer you would have got by sending the request and being refused.\n\nAffordable means all three at once: `provisioning_state` is `ready`, `recovery_action` is null, and `credit_shortfall` is 0. A zero shortfall is not enough on its own - while this period's included credits are still landing the state is `pending`, and the request is refused however healthy the balance looks, with nothing to do about it but wait for `provisioning_deadline` and quote again.\n\nAbsent on a key that is not credit-metered - an organization key is logged rather than debited, so it is never refused for credits and has nothing to recover from."
              }
            ]
          }
        },
        "required": [
          "credits",
          "estimated_ms"
        ],
        "description": "What a request would cost, whether the account can fund it, roughly how long it would take, and - when it asks for stems - what it would produce."
      },
      "PlannedOutput": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "mix",
              "speech",
              "music",
              "sfx",
              "ambience"
            ],
            "description": "What kind of audio this is. Treat this list as open: handle a kind you do not recognise rather than failing.",
            "x-speakeasy-unknown-values": "allow"
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "A specific name, when this output is one detected sound rather than a general kind of audio. Null otherwise."
          },
          "start_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where this clip belongs on the timeline, in milliseconds from the start of the source video, when the model placed it. For a separated sound this is the generated window, which reaches past the sound itself; see `sound_start_ms`."
          },
          "end_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where this clip ends on the timeline. See `start_ms`."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "foreground",
              "background",
              "swoosh",
              null
            ],
            "description": "The role a separated sound plays: `foreground` is a sound the on-screen action makes, `background` is ambience under the scene, and `swoosh` is a short sound that accompanies fast motion. Treat this list as open and handle a value you do not recognise. Null when the output is not a separated sound, and on a plan saved before this field existed.",
            "x-speakeasy-unknown-values": "allow"
          },
          "sound_start_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where the sound itself begins on the source video, in milliseconds. `start_ms` and `end_ms` are the generated window, which reaches past the sound to give each end some lead-in and tail audio, so trim the clip to this span and use the difference to crossfade. Null when the output is not a separated sound, and on a plan saved before this field existed."
          },
          "sound_end_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where the sound itself ends on the source video, in milliseconds. See `sound_start_ms`. Null in the same cases."
          },
          "credits": {
            "type": "integer",
            "description": "This output's share of the debit, on the same requested-count basis as the job's `credits` - not a per-delivered-variant price."
          },
          "variants_requested": {
            "type": "integer",
            "description": "How many alternatives were attempted for this output.\n\nOn a finished job, do not compare it with the length of `variants` to detect a partial failure. A shortfall takes two shapes and that comparison only sees one of them: a variant the model never produced is absent from `variants`, but one it produced whose file could not be described is present with `status: \"failed\"` and `files: null`, which leaves the lengths equal. Read this output's `status` for whether anything is missing, and each variant's `status` before reading its `files`."
          }
        },
        "required": [
          "index",
          "type",
          "label",
          "start_ms",
          "end_ms",
          "category",
          "sound_start_ms",
          "sound_end_ms",
          "credits",
          "variants_requested"
        ],
        "description": "One thing the request will produce, described before any audio exists. Preflight returns these; the finished job returns the same fields plus the audio."
      },
      "VideoToSfxJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "partially_succeeded",
              "failed",
              "canceled",
              "expired"
            ],
            "description": "Where the job is. Keep polling until it is final: `succeeded`, `partially_succeeded`, `failed`, `canceled` or `expired`.\n\nTreat this list as open and handle a state you do not recognise rather than failing - a new one is added when the pipeline grows a stage, not when the contract changes.",
            "x-speakeasy-unknown-values": "allow"
          },
          "model": {
            "type": "string",
            "description": "The public model release used for this job."
          },
          "seed": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the job reached a final state. Null while it is still `queued` or `running`, and null on an `expired` job that never reached one - expiry is the clock running out rather than an outcome, so there is nothing to report and nothing is invented. A fabricated one moved every time the same job was polled."
          },
          "expires_at": {
            "type": "string"
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Roughly how far along, as a fraction.\n\nDerived from elapsed time against `estimated_ms` rather than reported by the worker, so it moves smoothly and knows nothing about what inference is actually doing. It climbs towards 1 without reaching it, including for a job that outlives its estimate, so it never claims to be done - `status` is what says that.\n\nPresent while `queued` or `running` and absent once the job is final, so a finished job carries no number that reads as almost."
          },
          "estimated_ms": {
            "type": "integer",
            "description": "How long this job is expected to take, measured from `created_at`. Size your own timeout from this.\n\n`Retry-After` answers a different question - how long to wait before the **next** poll - and is capped at 30 seconds, so a job that will take three minutes says 30 there. This is the whole expected run.\n\nAn estimate from how long this shape of work has taken before, not a commitment: a job may finish well inside it or run well past it. Present while `queued` or `running` and dropped once the job is final, the same way `progress` is, and absent for the moment between a create being accepted and its row landing - there is nothing to estimate from yet."
          },
          "credits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What was actually debited, which is not always what the outputs suggest.\n\nBilling is all-or-nothing on the variant count you **requested**: a job that asked for three and delivered one is still debited for three, so a `partially_succeeded` job reports the full charge rather than a share of it. Read each output's `status` and each variant's to see what you got; do not infer it from this number.\n\nA `failed` job can also report a charge, and one case is not a mistake: `result_unreadable` means the audio was generated - and debited - but could not be read back, so it cannot be handed over. This field reports what the ledger took rather than zero, because the debit is real; the error's `retryable: false` is what tells you not to pay for it twice.\n\n`null` while the job is not finished, and on an organization key, which is logged rather than debited. `0` when no debit was recorded at all - including an `expired` job that never succeeded."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobError"
            }
          },
          "urls": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              }
            },
            "required": [
              "self"
            ]
          },
          "object": {
            "type": "string",
            "enum": [
              "video_to_sfx.generation"
            ]
          },
          "result": {
            "$ref": "#/components/schemas/OutputList"
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "created_at",
          "completed_at",
          "expires_at",
          "credits",
          "errors",
          "urls",
          "object",
          "result"
        ],
        "description": "A video-to-sfx job."
      },
      "OutputList": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Output"
            }
          }
        },
        "required": [
          "outputs"
        ]
      },
      "VideoToSfxRequest": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "The public model release to use. Required on every create and preflight, with no default or floating `latest` alias. `GET /v3/models` lists available releases and their supported operations.",
            "example": "sfx-1.6"
          },
          "duration_ms": {
            "type": "integer",
            "minimum": 1000,
            "description": "How much of the video to score, starting at `start_offset_ms`. The floor and the ceiling are per model, in `operations` on the model; when `controls.preserve_speech` is true, use `max_with_preserve_speech` instead of `max` where the model publishes one.",
            "example": 10000
          },
          "start_offset_ms": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Where in the source video to start, in milliseconds."
          },
          "num_variants": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "How many variations to generate. The ceiling is per model, in `operations` on the model. Asking for more than it allows is rejected rather than silently capped."
          },
          "seed": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4294967295,
            "description": "Starting point for the randomness. Omit it and we pick one, and report it back on the job. A seed narrows the result, it does not pin it."
          },
          "plan": {
            "type": "string",
            "minLength": 1,
            "description": "A `plan_id` from preflight. Required for controls.multi_stem, and refused for ordinary generation.\n\nGiven one, the parts it names are generated as quoted and no detection runs. `num_variants` is still yours to choose here - it is not part of the plan - and the quote scales with it.\n\nTwo ways it is rejected, and they mean different things:\n\n- `not_found` with `param: \"plan\"` - unknown id, expired, or someone else's. Run preflight again to get a new one.\n- `invalid_request` with `param: \"plan\"` - the plan exists, but the rest of this body is not what it was quoted for. Everything the plan was built from has to match: `model`, `input`, `duration_ms`, `start_offset_ms`, and the effective multi-stem mode. Send the body you preflighted.\n\nNeither case falls back to detecting quietly, so you cannot be billed for something other than what you were shown.",
            "example": "plan_4Qb8vT2nHk"
          },
          "input": {
            "$ref": "#/components/schemas/VideoInputBlock"
          },
          "controls": {
            "$ref": "#/components/schemas/VideoControls"
          },
          "output": {
            "$ref": "#/components/schemas/VideoOutputBlock"
          }
        },
        "required": [
          "model",
          "duration_ms",
          "input"
        ]
      },
      "VideoInputBlock": {
        "type": "object",
        "properties": {
          "video": {
            "$ref": "#/components/schemas/MediaRef"
          },
          "audio": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "description": "The clip's existing soundtrack, read only when speech is preserved: `controls.preserve_speech` is true, or left out on a model whose default keeps speech. Omit it and the track inside `input.video` is used, which is what an ordinary video wants; send it when the audio to preserve lives in a separate file, and keep that file aligned with the video because `start_offset_ms` selects the same window from both. Ignored entirely when speech is not preserved."
              }
            ]
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "description": "Unsupported with controls.multi_stem. Optional steer for ordinary generation. With one, the video drives the timing while the prompt guides which sound to generate; without, generation is video-driven only. Some models treat the prompt as experimental; see `operations.<family>.experimental`.",
            "example": "gravel footsteps, heavy and slow, in a stone corridor"
          },
          "negative_prompt": {
            "type": "string",
            "minLength": 1,
            "description": "Sounds you would rather not hear, as a short comma-separated list. Not every model takes one: where `max_negative_prompt_chars` on the model is 0, sending it is refused. Too long a negative prompt is rejected rather than truncated. Some models treat it as experimental; see `operations.<family>.experimental`.",
            "example": "music, speech, crowd noise"
          }
        },
        "required": [
          "video"
        ],
        "description": "What the model works from."
      },
      "MediaRef": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/AssetRef"
          },
          {
            "$ref": "#/components/schemas/UrlRef"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "asset": "#/components/schemas/AssetRef",
            "url": "#/components/schemas/UrlRef"
          }
        },
        "description": "The video to score. For best results use 16:9 footage; other ratios are accepted but get stretched before analysis."
      },
      "AssetRef": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "asset"
            ]
          },
          "id": {
            "type": "string",
            "minLength": 1,
            "description": "An asset id from `POST /v3/assets`. Use it only after your upload POST has returned `2xx`. Anything we will not generate from - still in flight, empty, or not yours - answers `asset_not_ready` without distinguishing which, so check the id as well as the timing."
          }
        },
        "required": [
          "type",
          "id"
        ]
      },
      "UrlRef": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "url"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "A public HTTPS URL we can fetch with a plain `GET`. Presigned links work: the file is identified from its own contents, so a link scoped to `GET` alone is fine and so is `application/octet-stream`."
          }
        },
        "required": [
          "type",
          "url"
        ]
      },
      "VideoControls": {
        "type": "object",
        "properties": {
          "multi_stem": {
            "type": "boolean",
            "description": "Generate an independent clip for each detected sound (magic mode). Preflight analyzes the video and returns a plan; create requires that plan. Outputs include each clip's label, timing and suggested gain. Supports wav and zero start_offset_ms only; cannot combine with preserve_speech. input.prompt and a non-null seed are unsupported. Defaults to ordinary generation when omitted."
          },
          "preserve_speech": {
            "type": "boolean",
            "description": "Keep dialogue already in your clip: generated sound is placed around it and ducked under it rather than competing with it. The speech comes from the clip's own soundtrack unless you send `input.audio`. Left out, the model's default applies: off on generally available models, while a preview model may keep speech unless told otherwise, so send `false` if you never want it. Where a model separates speech in a pass of its own, a clip carrying no audio at all is refused with `invalid_video` rather than coming back having preserved nothing, the duration ceiling is `max_with_preserve_speech`, and preflight reserves the upper-bound price, with the settled charge dropping to the base generation price when no speech is detected."
          }
        },
        "description": "Model-gated behaviour. Check `controls` on the model before relying on one."
      },
      "VideoOutputBlock": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "wav",
              "flac",
              "mp3_128",
              "mp3_192",
              "mp3_256",
              "mp3_320",
              "m4a_128",
              "m4a_192",
              "m4a_256"
            ],
            "default": "wav",
            "description": "How the audio comes back. Container and, for the lossy ones, encoding rate in kbps. `wav` and `flac` are lossless. `m4a` is AAC in an MP4 container. Sample rate is not selectable: generated audio comes back at the model's native rate, which is on the model as `audio.sample_rate`. Every file carries its own `sample_rate` and `channels`, which are not always the model's - the `speech` stem under `controls.preserve_speech` is separated out of your own audio rather than generated, so its rate is the separation's and today that is 48 kHz. Read the file's own fields before muxing or encoding."
          }
        },
        "description": "What comes back."
      },
      "ExtendJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "partially_succeeded",
              "failed",
              "canceled",
              "expired"
            ],
            "description": "Where the job is. Keep polling until it is final: `succeeded`, `partially_succeeded`, `failed`, `canceled` or `expired`.\n\nTreat this list as open and handle a state you do not recognise rather than failing - a new one is added when the pipeline grows a stage, not when the contract changes.",
            "x-speakeasy-unknown-values": "allow"
          },
          "model": {
            "type": "string",
            "description": "The public model release used for this job."
          },
          "seed": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the job reached a final state. Null while it is still `queued` or `running`, and null on an `expired` job that never reached one - expiry is the clock running out rather than an outcome, so there is nothing to report and nothing is invented. A fabricated one moved every time the same job was polled."
          },
          "expires_at": {
            "type": "string"
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Roughly how far along, as a fraction.\n\nDerived from elapsed time against `estimated_ms` rather than reported by the worker, so it moves smoothly and knows nothing about what inference is actually doing. It climbs towards 1 without reaching it, including for a job that outlives its estimate, so it never claims to be done - `status` is what says that.\n\nPresent while `queued` or `running` and absent once the job is final, so a finished job carries no number that reads as almost."
          },
          "estimated_ms": {
            "type": "integer",
            "description": "How long this job is expected to take, measured from `created_at`. Size your own timeout from this.\n\n`Retry-After` answers a different question - how long to wait before the **next** poll - and is capped at 30 seconds, so a job that will take three minutes says 30 there. This is the whole expected run.\n\nAn estimate from how long this shape of work has taken before, not a commitment: a job may finish well inside it or run well past it. Present while `queued` or `running` and dropped once the job is final, the same way `progress` is, and absent for the moment between a create being accepted and its row landing - there is nothing to estimate from yet."
          },
          "credits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What was actually debited, which is not always what the outputs suggest.\n\nBilling is all-or-nothing on the variant count you **requested**: a job that asked for three and delivered one is still debited for three, so a `partially_succeeded` job reports the full charge rather than a share of it. Read each output's `status` and each variant's to see what you got; do not infer it from this number.\n\nA `failed` job can also report a charge, and one case is not a mistake: `result_unreadable` means the audio was generated - and debited - but could not be read back, so it cannot be handed over. This field reports what the ledger took rather than zero, because the debit is real; the error's `retryable: false` is what tells you not to pay for it twice.\n\n`null` while the job is not finished, and on an organization key, which is logged rather than debited. `0` when no debit was recorded at all - including an `expired` job that never succeeded."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobError"
            }
          },
          "urls": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              }
            },
            "required": [
              "self"
            ]
          },
          "object": {
            "type": "string",
            "enum": [
              "extend.edit"
            ]
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "output": {
                "$ref": "#/components/schemas/Output"
              }
            },
            "required": [
              "output"
            ]
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "created_at",
          "completed_at",
          "expires_at",
          "credits",
          "errors",
          "urls",
          "object",
          "result"
        ],
        "description": "An extend edit."
      },
      "ExtendRequest": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "The public model release to use. Required on every create and preflight, with no default or floating `latest` alias. `GET /v3/models` lists available releases and their supported operations.",
            "example": "sfx-1.6"
          },
          "prepend_duration_ms": {
            "type": "integer",
            "description": "How much new audio to add before your source. Not served yet - sending it is refused with `capability_unsupported`, and `operations.extend` on the model omits the key to match. Read the model rather than assuming."
          },
          "append_duration_ms": {
            "type": "integer",
            "minimum": 1000,
            "description": "How much new audio to add after your source, in milliseconds. Not the total length of the result - that is your source plus this, and it comes back measured on the returned file.\n\nThe floor moves with `controls.loop` and the ceiling is per model; both are in `operations.extend` on the model. Credits are charged on this alone, never on the length of the result.",
            "example": 8000
          },
          "start_offset_ms": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Where in `input.video` your audio begins, in milliseconds. Defaults to 0, the start of the clip - so a prefix taken from thirty seconds into the footage is `30000` rather than a clip you have to trim first.\n\nOnly meaningful with a clip. A non-zero offset and no `input.video` is refused rather than ignored, because there is nothing for it to move."
          },
          "num_variants": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "How many variations to generate. The ceiling is per model, in `operations` on the model. Asking for more than it allows is rejected rather than silently capped."
          },
          "seed": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4294967295,
            "description": "Starting point for the randomness. Omit it and we pick one, and report it back on the job. A seed narrows the result, it does not pin it."
          },
          "input": {
            "$ref": "#/components/schemas/EditInputBlock"
          },
          "controls": {
            "$ref": "#/components/schemas/LoopControls"
          },
          "output": {
            "$ref": "#/components/schemas/AudioOutputBlock"
          }
        },
        "required": [
          "model",
          "input"
        ],
        "description": "Continues your audio past where it stops.\n\nThree rules the server enforces rather than the schema, because none of them is a shape JSON Schema can express without making the request untypeable for generated clients:\n\n1. **Send `append_duration_ms`.** A request with no duration to add has nothing to do.\n2. **`controls.loop` and `input.video` are mutually exclusive.** Looping closes the result back onto its own start, which is not something footage can guide.\n3. **`start_offset_ms` needs `input.video`.** It says where in the clip your audio begins, so with no clip there is nothing for it to move.\n\nYour source must be at least 3 seconds long, and the result is capped at 60 seconds. A longer source is not refused: only the last part of it conditions the model, and the untouched head is joined back on afterwards."
      },
      "EditInputBlock": {
        "type": "object",
        "properties": {
          "audio": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "description": "The audio to work from. Everything outside the part being generated comes back exactly as you sent it, so this is your source, not a style reference."
              }
            ]
          },
          "video": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "description": "Optional. Lets the new audio follow what happens on screen. The clip is read from where your audio starts - on extend, `start_offset_ms` moves that point - and has to cover the part being generated: your source plus the new section on extend, the section you are replacing on inpaint. On extend it cannot be combined with `controls.loop`."
              }
            ]
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "description": "Optional steer for the new audio. Describe the sound, not the picture.",
            "example": "gravel footsteps, heavy and slow, in a stone corridor"
          }
        },
        "required": [
          "audio"
        ],
        "description": "What the model works from."
      },
      "InpaintJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "partially_succeeded",
              "failed",
              "canceled",
              "expired"
            ],
            "description": "Where the job is. Keep polling until it is final: `succeeded`, `partially_succeeded`, `failed`, `canceled` or `expired`.\n\nTreat this list as open and handle a state you do not recognise rather than failing - a new one is added when the pipeline grows a stage, not when the contract changes.",
            "x-speakeasy-unknown-values": "allow"
          },
          "model": {
            "type": "string",
            "description": "The public model release used for this job."
          },
          "seed": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the job reached a final state. Null while it is still `queued` or `running`, and null on an `expired` job that never reached one - expiry is the clock running out rather than an outcome, so there is nothing to report and nothing is invented. A fabricated one moved every time the same job was polled."
          },
          "expires_at": {
            "type": "string"
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Roughly how far along, as a fraction.\n\nDerived from elapsed time against `estimated_ms` rather than reported by the worker, so it moves smoothly and knows nothing about what inference is actually doing. It climbs towards 1 without reaching it, including for a job that outlives its estimate, so it never claims to be done - `status` is what says that.\n\nPresent while `queued` or `running` and absent once the job is final, so a finished job carries no number that reads as almost."
          },
          "estimated_ms": {
            "type": "integer",
            "description": "How long this job is expected to take, measured from `created_at`. Size your own timeout from this.\n\n`Retry-After` answers a different question - how long to wait before the **next** poll - and is capped at 30 seconds, so a job that will take three minutes says 30 there. This is the whole expected run.\n\nAn estimate from how long this shape of work has taken before, not a commitment: a job may finish well inside it or run well past it. Present while `queued` or `running` and dropped once the job is final, the same way `progress` is, and absent for the moment between a create being accepted and its row landing - there is nothing to estimate from yet."
          },
          "credits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What was actually debited, which is not always what the outputs suggest.\n\nBilling is all-or-nothing on the variant count you **requested**: a job that asked for three and delivered one is still debited for three, so a `partially_succeeded` job reports the full charge rather than a share of it. Read each output's `status` and each variant's to see what you got; do not infer it from this number.\n\nA `failed` job can also report a charge, and one case is not a mistake: `result_unreadable` means the audio was generated - and debited - but could not be read back, so it cannot be handed over. This field reports what the ledger took rather than zero, because the debit is real; the error's `retryable: false` is what tells you not to pay for it twice.\n\n`null` while the job is not finished, and on an organization key, which is logged rather than debited. `0` when no debit was recorded at all - including an `expired` job that never succeeded."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobError"
            }
          },
          "urls": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              }
            },
            "required": [
              "self"
            ]
          },
          "object": {
            "type": "string",
            "enum": [
              "inpaint.edit"
            ]
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "output": {
                "$ref": "#/components/schemas/Output"
              }
            },
            "required": [
              "output"
            ]
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "created_at",
          "completed_at",
          "expires_at",
          "credits",
          "errors",
          "urls",
          "object",
          "result"
        ],
        "description": "An inpaint edit."
      },
      "InpaintRequest": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "The public model release to use. Required on every create and preflight, with no default or floating `latest` alias. `GET /v3/models` lists available releases and their supported operations.",
            "example": "sfx-1.6"
          },
          "region": {
            "$ref": "#/components/schemas/InpaintRegion"
          },
          "num_variants": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "How many variations to generate. The ceiling is per model, in `operations` on the model. Asking for more than it allows is rejected rather than silently capped."
          },
          "seed": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4294967295,
            "description": "Starting point for the randomness. Omit it and we pick one, and report it back on the job. A seed narrows the result, it does not pin it."
          },
          "input": {
            "$ref": "#/components/schemas/EditInputBlock"
          },
          "output": {
            "$ref": "#/components/schemas/AudioOutputBlock"
          }
        },
        "required": [
          "model",
          "region",
          "input"
        ],
        "description": "Replaces a section of your audio, keeping the rest of the file exactly as it was.\n\nThe new audio is generated from what surrounds the gap, so it lands in context rather than being dropped in. Send `input.video` to have it follow what happens on screen, and a prompt to say what should be there instead.\n\nThree rules the server enforces rather than the schema, because each is a bound the model publishes rather than a shape: where the section may start, how wide it may be, and that it has to end inside the file you sent. Read the first two from `operations.inpaint` on the model; the third is checked once your audio has been read, so it can only be answered by the real call.\n\nYou are billed on the width of the section you replace, never on the length of your file."
      },
      "InpaintRegion": {
        "type": "object",
        "properties": {
          "start_ms": {
            "type": "integer",
            "minimum": 0,
            "description": "Where the replaced section begins. The earliest it may be is per model, in `operations.inpaint.region_start_ms` - a model that conditions on the audio ahead of the gap cannot replace the very start of your file.",
            "example": 2000
          },
          "end_ms": {
            "type": "integer",
            "minimum": 0,
            "description": "Where it ends. May equal the length of your audio - the section can run to the end of the file. `end_ms` minus `start_ms` is the width, and the band it has to fall in is per model, in `operations.inpaint.region_width_ms`.",
            "example": 4000
          }
        },
        "required": [
          "start_ms",
          "end_ms"
        ],
        "description": "The part of your audio to replace."
      },
      "ModelList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "Model": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The public model release id used in request `model` fields."
          },
          "release_date": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "preview",
              "general_availability",
              "deprecated"
            ]
          },
          "audio": {
            "type": "object",
            "properties": {
              "sample_rate": {
                "type": "integer"
              },
              "channels": {
                "type": "integer"
              }
            },
            "required": [
              "sample_rate",
              "channels"
            ]
          },
          "max_prompt_chars": {
            "type": "integer"
          },
          "max_negative_prompt_chars": {
            "type": "integer",
            "description": "The longest `input.negative_prompt` this model takes on text-to-sfx and video-to-sfx, in characters. 0 means it takes none, and a request that sends one is refused with `capability_unsupported`. Extend and inpaint take none on any model and refuse one the same way."
          },
          "stems": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "controls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "formats": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The `output.format` tokens this deployment will actually deliver. The request schema's enum is wider on purpose - it is the shape of every token the API reserves - and a token in the enum but absent here answers `capability_unsupported`, so read the set from here rather than from the enum.\n\nSeparated stems are the one narrowing this list does not describe: every stem is generated by its own lossless pass, so `controls.multi_stem` accepts `wav` alone whatever this says.",
            "example": [
              "wav",
              "flac",
              "mp3_320"
            ]
          },
          "operations": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/ModelOperationLimits"
            },
            "description": "Which endpoints accept this model, and the limits for each. A family that is absent is not served. Inside a family, an absent limit key means that parameter is not served either."
          },
          "credits_per_second": {
            "type": "number",
            "description": "The base generation rate to compute a price offline, if you would rather not call preflight.\n\nWithout a separately priced control, `credits = ceil(billed_ms / 1000 * num_variants * credits_per_second)`, and that is exactly what the server charges.\n\n`billed_ms` is the NEW audio you asked for, which is a different quantity per family: `duration_ms` where the whole result is generated, `append_duration_ms` on extend, which charges for what it adds and never for the source you already had, and on inpaint the width of the section you are replacing - `region.end_ms` minus `region.start_ms`. Sizing either edit off the length of the result would overquote it by the whole source you sent. `controls.loop` does not change the rate. Read it from here rather than hardcoding it.\n\n`controls.preserve_speech` adds a duration-based separation surcharge to the preflight upper bound. When no speech is detected, the settled charge drops back to the base formula above; when speech is preserved, the surcharge remains. Use preflight rather than this field to budget a preserve-speech request, and read the completed job's `credits` for its settled charge.\n\nPreflight is still the authority when you want the number plus an ETA; this field is for sizing an unsurcharged budget without a round trip."
          }
        },
        "required": [
          "id",
          "status",
          "audio",
          "max_prompt_chars",
          "max_negative_prompt_chars",
          "stems",
          "controls",
          "formats",
          "operations",
          "credits_per_second"
        ],
        "description": "A public model release and the capability contract it accepts."
      },
      "ModelOperationLimits": {
        "type": "object",
        "properties": {
          "duration_ms": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              },
              "min_with_loop": {
                "type": "integer"
              },
              "max_with_loop": {
                "type": "integer"
              },
              "max_with_preserve_speech": {
                "type": "integer"
              }
            }
          },
          "prepend_duration_ms": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            },
            "description": "Extend only, and absent on a model that cannot generate before your source. No model publishes it today."
          },
          "append_duration_ms": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "min_with_loop": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            },
            "description": "Extend only. How much NEW audio may be added after your source, which is also what the request is billed on - never the length of the result, which is your source plus this. `min_with_loop` replaces `min` when `controls.loop` is on; the ceiling does not move with it."
          },
          "region_start_ms": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              }
            },
            "description": "Inpaint only. The earliest the replaced section may start, for a model that conditions on the audio ahead of the gap."
          },
          "region_width_ms": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            },
            "description": "Inpaint only. How wide the replaced section may be - `region.end_ms` minus `region.start_ms`, which is also what the request is billed on. Never the length of the result, which is your whole source with that section replaced."
          },
          "num_variants": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            }
          },
          "controls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "experimental": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Request inputs this model accepts but does not always follow, as dotted request paths such as `input.prompt`. They usually steer the result; check what comes back rather than relying on them. Absent means none.",
            "example": [
              "input.negative_prompt"
            ]
          }
        }
      },
      "AssetTicket": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Pass this as `{ type: \"asset\", id }` wherever a request takes media."
          },
          "upload_url": {
            "type": "string",
            "format": "uri",
            "description": "POST a `multipart/form-data` body here - not a PUT, and with no `Authorization` header. The upload is authorized by the `fields`, not by this URL."
          },
          "upload_expires_at": {
            "type": "string",
            "description": "When the upload grant stops working. Read off the policy in `fields`, so it is the moment storage itself stops honouring it rather than an estimate. This bounds when you may start the upload, not how long the asset lasts afterwards."
          },
          "max_bytes": {
            "type": "integer",
            "description": "The largest file to send. Read it from here rather than assuming - it is per-request and can differ from the example. It is a condition of the upload policy, so storage refuses an oversized body itself with a `400` and nothing is stored; the same limit is re-checked when a generation uses the asset, which answers `payload_too_large`.\n\nIt is sized off the longest window a model publishes for a file it reads, so a window you may ask for is a window you can upload for. Two shapes need more file than that window: a window taken from further in, since `start_offset_ms` is skipped before it begins, and the edit endpoints, which bound what they generate and put no bound at all on the source it is added to. Both are legitimate requests. For those, and for anything else over the limit, serve the file yourself and pass `{ \"type\": \"url\" }` wherever a request takes media - a URL has no size limit, because the bytes are never ours."
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The form fields to send, each exactly as given and **all of them before** the `file` part - S3 ignores anything that follows the file. They carry the policy and its signature, so an edited value is refused rather than stored. Send your bytes as the last part, named `file`. Treat the set as opaque and echo it back: what has to be signed is a storage decision and can change."
          }
        },
        "required": [
          "id",
          "upload_url",
          "upload_expires_at",
          "max_bytes",
          "fields"
        ],
        "description": "Where to upload your file, and until when."
      },
      "CreateAssetRequest": {
        "type": "object",
        "properties": {
          "content_type": {
            "type": "string",
            "pattern": "^(audio|video)\\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$",
            "description": "The MIME type of the file you are about to upload. A plain `audio/*` or `video/*` type with no parameters - a charset or codecs clause cannot be signed into the upload policy. It comes back as one of the `fields`, and the policy pins it, so the value you send has to be this one.",
            "example": "video/mp4"
          }
        },
        "required": [
          "content_type"
        ]
      },
      "Account": {
        "type": "object",
        "properties": {
          "account_type": {
            "type": "string",
            "enum": [
              "user",
              "organization"
            ]
          },
          "id": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address on the account, so a client holding a key can show whose account it is. Nothing else on this body depends on it and it changes no funding decision - it is here because a key is the only credential these clients have, and an id is not something to show a person.\n\nNull whenever there is no user behind the key to name. An organization key is billed to the organization and has no payer user, which is the same reason its credit figures are null."
          },
          "credits_available": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The ledger balance. What has been granted and not yet debited, which is what reconciles against an invoice - not what the next request may spend. Use `spend_capacity` to decide whether a request will be funded.\n\nNull on unmetered plans, where usage is logged rather than debited."
          },
          "spend_capacity": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What a request may actually spend right now: the ledger balance, less credits already committed by jobs in flight, plus any enabled overage headroom. This is the number to compare a preflight `credits` against.\n\nIt can be lower than `credits_available` while your own jobs are running, and higher than it when overage is enabled. Zero while provisioning blocks spending or on an account with no credit-spending plan. Zero capacity with no recovery action means provisioning is pending. Null on unmetered plans."
          },
          "recovery_action": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "enable_overage",
              "increase_overage_limit",
              "view_plans",
              "contact_support",
              null
            ],
            "description": "The one step that would fix an account that cannot fund even a one-credit request. Null when nothing needs doing, and on unmetered plans, which are never refused - read `billing_mode` to tell those apart.\n\nThe same values a 402's `error.credit_recovery` carries, so a client can offer the step before a request is refused rather than after."
          },
          "recovery_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Where to do it. Null exactly when `recovery_action` is."
          },
          "billing_mode": {
            "type": "string",
            "enum": [
              "metered",
              "unmetered"
            ]
          },
          "provisioning_state": {
            "type": "string",
            "enum": [
              "ready",
              "pending",
              "stalled"
            ],
            "description": "Account readiness at response time. Pending and stalled block spending. Ready on unmetered accounts."
          },
          "provisioning_deadline": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix epoch milliseconds when pending provisioning becomes stalled. Clients can update the support message at this deadline without another request. Null when the grant is fulfilled or provisioning does not apply."
          }
        },
        "required": [
          "account_type",
          "id",
          "email",
          "credits_available",
          "spend_capacity",
          "recovery_action",
          "recovery_url",
          "billing_mode",
          "provisioning_state",
          "provisioning_deadline"
        ],
        "description": "Which account this key belongs to, and whether the next request will be funded."
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v3/text-to-sfx/generations": {
      "post": {
        "tags": [
          "Generate"
        ],
        "operationId": "createTextToSfx",
        "summary": "Generate sound from a description",
        "description": "Describe a sound and get it back. No video involved. Use this when you know what you want to hear: UI clicks, weapon foley, ambiences, transitions.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to hold the request while the job runs, 1 to 25. Omit to return immediately. Out of range is rejected rather than clamped."
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^wait=(?:1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25)$"
            },
            "description": "`wait=<seconds>`, the RFC 7240 spelling of the `wait` query parameter, 1 to 25. If you send both, the query parameter wins."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "A unique string of your choosing.\n\nThe same key with the same body returns the original job instead of charging twice, and carries `Idempotent-Replayed: true`. It is also how you recover a job id if the connection drops while the server is holding your request. The same key with a *different* body is `409` - and bodies are compared **as you sent them**, so an extra or misspelled field counts as a difference even though the schemas themselves ignore unknown keys.\n\nFour things that are easy to assume the other way round:\n\n- **A key is scoped to one endpoint.** It is remembered per API key *and* per path, so the same key sent to two different collections creates two jobs and charges for both. Do not reuse one key across endpoints for what you think of as a single operation.\n- **A replay is re-rendered, not a stored response.** You get the job as it stands now, with fresh download links and a freshly read `Mirelo-Concurrency-Current`, so a create that answered `202` replays as `200` once the job has finished. The job is guaranteed to be the original one; the bytes of the response are not guaranteed to match.\n- **A failed create does not burn the key.** A create refused before the job runs, or one that fails while starting it - insufficient credits, an unusable asset, the concurrency ceiling - hands the key back, so nothing failed is ever stored or replayed and the same key is immediately safe to retry. The exception is a failure *after* the job is running, where the key stays held: retrying then gets the job, or a `503` asking you to try again in a moment while it is still being accepted.\n- **The window is a range, not exactly 24 hours.** A key is eligible to be forgotten 24 hours after the request that used it, and a daily sweep does the forgetting, so in practice it is remembered for 24 to 48 hours depending on when it was used. After that, reusing it starts new work and charges again."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TextToSfxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Finished inside the wait window, or a replay of an earlier request with the same idempotency key that has already finished.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextToSfxJob"
                }
              }
            }
          },
          "202": {
            "description": "Accepted and running, or a replay of an earlier request that has not finished yet. Poll `urls.self` after `Retry-After` seconds.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextToSfxJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active subscription, or not enough credits. `error.credit_recovery` says how many credits the request needed, what the account can actually spend, and which billing step fixes it. Every refusal the credit check produces carries it, which today is all of them - handle its absence rather than assuming it, since a future 402 raised without reading the ledger would have no figures to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredError"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "An idempotency key was reused with a different request body. Reusing one with the *same* body is not an error - it returns the original job.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/text-to-sfx/generations/{id}": {
      "get": {
        "tags": [
          "Generate"
        ],
        "operationId": "getTextToSfx",
        "summary": "Fetch a text-to-sfx generation",
        "description": "Returns the job in whatever state it is in. Keep fetching until `status` is final.\n\nAdd `?wait=25` to long-poll. A wait that runs out of time is not an error and not a different status code: you get `200` with a `status` that is still `queued` or `running`, plus `Retry-After` for the next attempt. Only create uses `202` to mean in progress.\n\nDownload links expire. Fetch the job again for fresh ones.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The job id. Take it from `urls.self` on the create response."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to wait for a final state before responding."
          }
        ],
        "responses": {
          "200": {
            "description": "The job, in whatever state it is in. A job older than its `expires_at` answers here too, with `status: \"expired\"` and no result, so an aged-out job stays distinguishable from an id that never existed. `Retry-After` is present while the job is still running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextToSfxJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No job with that id. `param` is `\"id\"`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/text-to-sfx/generations/preflight": {
      "post": {
        "tags": [
          "Generate"
        ],
        "operationId": "preflightTextToSfx",
        "summary": "Check cost and affordability before running",
        "description": "Send exactly the body you intend to send to the real endpoint and get back what it would cost, whether your account can fund it, and roughly how long it would take. Nothing is generated and nothing is charged.\n\n`credit_recovery` answers the funding question for **this** body, and a non-null `recovery_action` is the same step the `402` would have given you - so there is no need to spend a failed request to find out what to tell the customer. It is absent on a key that is not credit-metered, which is never refused for credits.\n\nThe request is affordable when all three hold: `provisioning_state` is `ready`, `recovery_action` is null, and `credit_shortfall` is 0. Do not read the shortfall on its own - a `pending` grant refuses the request at a shortfall of 0, and the only thing to do about that is wait for `provisioning_deadline` and quote again.\n\nIt runs the same validation on the same body, and the body is the whole request here, so anything preflight accepts the real call accepts too.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TextToSfxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cost, affordability and estimate.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Preflight"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/video-to-sfx/generations": {
      "post": {
        "tags": [
          "Generate"
        ],
        "operationId": "createVideoToSfx",
        "summary": "Generate sound for a video",
        "description": "Score a clip. The model watches the footage and generates sound that lands on what happens in it, so the timing comes from the video rather than from you. A prompt is optional: with one, the video still drives the timing while the prompt guides which sound to make.\n\nPass the video as a URL we can fetch, or as an `asset` id from `POST /v3/assets`.\n\nFor magic mode, which detects sounds and generates each independently, ask preflight first: send the same body with `controls.multi_stem` set to `true`, show the parts it found, then send that request again with its `plan_id` as `plan`. You get one output per part, at the price you were quoted. There is no second way in - a create asking for separated parts without a plan is refused rather than detecting again and charging for a set you were never shown.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to hold the request while the job runs, 1 to 25. Omit to return immediately. Out of range is rejected rather than clamped."
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^wait=(?:1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25)$"
            },
            "description": "`wait=<seconds>`, the RFC 7240 spelling of the `wait` query parameter, 1 to 25. If you send both, the query parameter wins."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "A unique string of your choosing.\n\nThe same key with the same body returns the original job instead of charging twice, and carries `Idempotent-Replayed: true`. It is also how you recover a job id if the connection drops while the server is holding your request. The same key with a *different* body is `409` - and bodies are compared **as you sent them**, so an extra or misspelled field counts as a difference even though the schemas themselves ignore unknown keys.\n\nFour things that are easy to assume the other way round:\n\n- **A key is scoped to one endpoint.** It is remembered per API key *and* per path, so the same key sent to two different collections creates two jobs and charges for both. Do not reuse one key across endpoints for what you think of as a single operation.\n- **A replay is re-rendered, not a stored response.** You get the job as it stands now, with fresh download links and a freshly read `Mirelo-Concurrency-Current`, so a create that answered `202` replays as `200` once the job has finished. The job is guaranteed to be the original one; the bytes of the response are not guaranteed to match.\n- **A failed create does not burn the key.** A create refused before the job runs, or one that fails while starting it - insufficient credits, an unusable asset, the concurrency ceiling - hands the key back, so nothing failed is ever stored or replayed and the same key is immediately safe to retry. The exception is a failure *after* the job is running, where the key stays held: retrying then gets the job, or a `503` asking you to try again in a moment while it is still being accepted.\n- **The window is a range, not exactly 24 hours.** A key is eligible to be forgotten 24 hours after the request that used it, and a daily sweep does the forgetting, so in practice it is remembered for 24 to 48 hours depending on when it was used. After that, reusing it starts new work and charges again."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoToSfxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Finished inside the wait window, or a replay of an earlier request with the same idempotency key that has already finished.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoToSfxJob"
                }
              }
            }
          },
          "202": {
            "description": "Accepted and running, or a replay of an earlier request that has not finished yet. Poll `urls.self` after `Retry-After` seconds.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoToSfxJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active subscription, or not enough credits. `error.credit_recovery` says how many credits the request needed, what the account can actually spend, and which billing step fixes it. Every refusal the credit check produces carries it, which today is all of them - handle its absence rather than assuming it, since a future 402 raised without reading the ledger would have no figures to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredError"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No plan with that `plan_id` - unknown, expired, or minted for another key. `param` is `\"plan\"`. Preflight the same body again to get a new one.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Either an idempotency key was reused with a different request body, or an asset in `input` is not usable yet - `code` says which. Reusing a key with the *same* body is not an error: it returns the original job.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "An uploaded asset is larger than the `max_bytes` that `POST /v3/assets` published. The upload policy refuses an oversized body at upload time, so reaching this means the object was staged some other way.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The file was fetched but cannot be used - the wrong kind of media for the field it was sent in, an asset that storage has but this endpoint cannot read, or a clip that does not carry what the request needs out of it, as a video with no audio track does not under `controls.preserve_speech`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/video-to-sfx/generations/{id}": {
      "get": {
        "tags": [
          "Generate"
        ],
        "operationId": "getVideoToSfx",
        "summary": "Fetch a video-to-sfx generation",
        "description": "Returns the job in whatever state it is in. Keep fetching until `status` is final.\n\nAdd `?wait=25` to long-poll. A wait that runs out of time is not an error and not a different status code: you get `200` with a `status` that is still `queued` or `running`, plus `Retry-After` for the next attempt. Only create uses `202` to mean in progress.\n\nDownload links expire. Fetch the job again for fresh ones.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The job id. Take it from `urls.self` on the create response."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to wait for a final state before responding."
          }
        ],
        "responses": {
          "200": {
            "description": "The job, in whatever state it is in. A job older than its `expires_at` answers here too, with `status: \"expired\"` and no result, so an aged-out job stays distinguishable from an id that never existed. `Retry-After` is present while the job is still running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoToSfxJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No job with that id. `param` is `\"id\"`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/video-to-sfx/generations/preflight": {
      "post": {
        "tags": [
          "Generate"
        ],
        "operationId": "preflightVideoToSfx",
        "summary": "Check cost and affordability before running",
        "description": "Send exactly the body you intend to send to the real endpoint and get back what it would cost, whether your account can fund it, and roughly how long it would take. Nothing is generated and nothing is charged.\n\n`credit_recovery` answers the funding question for **this** body, and a non-null `recovery_action` is the same step the `402` would have given you - so there is no need to spend a failed request to find out what to tell the customer. It is absent on a key that is not credit-metered, which is never refused for credits.\n\nThe request is affordable when all three hold: `provisioning_state` is `ready`, `recovery_action` is null, and `credit_shortfall` is 0. Do not read the shortfall on its own - a `pending` grant refuses the request at a shortfall of 0, and the only thing to do about that is wait for `provisioning_deadline` and quote again.\n\nIt is a quote, not an admission ticket. The model, duration, variant count, format and stems are validated exactly as create validates them, so a rejection here is a rejection there. What a preflight without stems does **not** do is read the file your `input` points at: an `asset` id is not checked for ownership, for having landed, or against `max_bytes`, and a `url` is not fetched. (Asking for stems is the exception, and the paragraph below says how.) Create checks all of that, so a `200` here can still be followed on the real call by `asset_not_ready`, `payload_too_large`, `invalid_asset`, `video_url_unreachable`, `video_format_unsupported`, `video_too_short`, `invalid_video`, or `invalid_request` for a URL the fetcher refuses (a private or non-public address, for instance). Branch on `code` rather than on this list being complete. Nothing is charged either way.\n\nAsk for separated stems and this is no longer only arithmetic: it fetches your clip, runs sound detection over it, and answers with `outputs` - the parts it found, each with its own cost and its place on the timeline - plus a `plan_id` and the moment that id stops working. Send the same body back to the real endpoint with that id as `plan` and those parts are generated at that price, without detecting a second time. Because that pass is real work, a stems preflight carries a tighter rate limit than a plain one and can answer `429` where a plain one would not. Do not call it on every keystroke. It is also the one preflight that does read your file, so an `asset` in `input.video` gets the same checks create gives it - ownership, having landed, and `max_bytes` - and can answer `asset_not_ready`, `payload_too_large` or `invalid_asset` where a plain preflight would have priced the request without looking.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoToSfxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cost, affordability and estimate.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Preflight"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "An asset in `input` is not usable yet. Only a quote that asks for separated stems can answer this, because that is the only one that reads your file - and unlike the create's 409 it is never an idempotency conflict, since a quote takes no `Idempotency-Key`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "An uploaded asset is larger than the `max_bytes` that `POST /v3/assets` published. The upload policy refuses an oversized body at upload time, so reaching this means the object was staged some other way.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The file was fetched but cannot be used - the wrong kind of media for the field it was sent in, an asset that storage has but this endpoint cannot read, or a clip that does not carry what the request needs out of it, as a video with no audio track does not under `controls.preserve_speech`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/extend/edits": {
      "post": {
        "tags": [
          "Edit"
        ],
        "operationId": "createExtend",
        "summary": "Add audio to the end of a clip",
        "description": "Continues your audio past where it stops. `append_duration_ms` is how much NEW audio to add, never the total - the result is your source plus that, and its real length comes back measured on the returned file.\n\nPass `input.video` to have the new audio follow what happens on screen, and `controls.loop` to have it land back on the start of your source so the whole thing loops. Those two are mutually exclusive.\n\nYour source needs to be at least 3 seconds long. A longer one is fine: only its last part conditions the model, and the untouched head is joined back on afterwards.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to hold the request while the job runs, 1 to 25. Omit to return immediately. Out of range is rejected rather than clamped."
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^wait=(?:1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25)$"
            },
            "description": "`wait=<seconds>`, the RFC 7240 spelling of the `wait` query parameter, 1 to 25. If you send both, the query parameter wins."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "A unique string of your choosing.\n\nThe same key with the same body returns the original job instead of charging twice, and carries `Idempotent-Replayed: true`. It is also how you recover a job id if the connection drops while the server is holding your request. The same key with a *different* body is `409` - and bodies are compared **as you sent them**, so an extra or misspelled field counts as a difference even though the schemas themselves ignore unknown keys.\n\nFour things that are easy to assume the other way round:\n\n- **A key is scoped to one endpoint.** It is remembered per API key *and* per path, so the same key sent to two different collections creates two jobs and charges for both. Do not reuse one key across endpoints for what you think of as a single operation.\n- **A replay is re-rendered, not a stored response.** You get the job as it stands now, with fresh download links and a freshly read `Mirelo-Concurrency-Current`, so a create that answered `202` replays as `200` once the job has finished. The job is guaranteed to be the original one; the bytes of the response are not guaranteed to match.\n- **A failed create does not burn the key.** A create refused before the job runs, or one that fails while starting it - insufficient credits, an unusable asset, the concurrency ceiling - hands the key back, so nothing failed is ever stored or replayed and the same key is immediately safe to retry. The exception is a failure *after* the job is running, where the key stays held: retrying then gets the job, or a `503` asking you to try again in a moment while it is still being accepted.\n- **The window is a range, not exactly 24 hours.** A key is eligible to be forgotten 24 hours after the request that used it, and a daily sweep does the forgetting, so in practice it is remembered for 24 to 48 hours depending on when it was used. After that, reusing it starts new work and charges again."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Finished inside the wait window, or a replay of an earlier request with the same idempotency key that has already finished.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtendJob"
                }
              }
            }
          },
          "202": {
            "description": "Accepted and running, or a replay of an earlier request that has not finished yet. Poll `urls.self` after `Retry-After` seconds.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtendJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active subscription, or not enough credits. `error.credit_recovery` says how many credits the request needed, what the account can actually spend, and which billing step fixes it. Every refusal the credit check produces carries it, which today is all of them - handle its absence rather than assuming it, since a future 402 raised without reading the ledger would have no figures to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredError"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Either an idempotency key was reused with a different request body, or an asset in `input` is not usable yet - `code` says which. Reusing a key with the *same* body is not an error: it returns the original job.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "An uploaded asset is larger than the `max_bytes` that `POST /v3/assets` published. The upload policy refuses an oversized body at upload time, so reaching this means the object was staged some other way.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The file was fetched but cannot be used - the wrong kind of media for the field it was sent in, an asset that storage has but this endpoint cannot read, or a clip that does not carry what the request needs out of it, as a video with no audio track does not under `controls.preserve_speech`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/extend/edits/{id}": {
      "get": {
        "tags": [
          "Edit"
        ],
        "operationId": "getExtend",
        "summary": "Fetch an extend edit",
        "description": "Returns the job in whatever state it is in. Keep fetching until `status` is final.\n\nAdd `?wait=25` to long-poll. A wait that runs out of time is not an error and not a different status code: you get `200` with a `status` that is still `queued` or `running`, plus `Retry-After` for the next attempt. Only create uses `202` to mean in progress.\n\nDownload links expire. Fetch the job again for fresh ones.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The job id. Take it from `urls.self` on the create response."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to wait for a final state before responding."
          }
        ],
        "responses": {
          "200": {
            "description": "The job, in whatever state it is in. A job older than its `expires_at` answers here too, with `status: \"expired\"` and no result, so an aged-out job stays distinguishable from an id that never existed. `Retry-After` is present while the job is still running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtendJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No job with that id. `param` is `\"id\"`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/extend/edits/preflight": {
      "post": {
        "tags": [
          "Edit"
        ],
        "operationId": "preflightExtend",
        "summary": "Check cost and affordability before running",
        "description": "Send exactly the body you intend to send to the real endpoint and get back what it would cost, whether your account can fund it, and roughly how long it would take. Nothing is generated and nothing is charged.\n\n`credit_recovery` answers the funding question for **this** body, and a non-null `recovery_action` is the same step the `402` would have given you - so there is no need to spend a failed request to find out what to tell the customer. It is absent on a key that is not credit-metered, which is never refused for credits.\n\nThe request is affordable when all three hold: `provisioning_state` is `ready`, `recovery_action` is null, and `credit_shortfall` is 0. Do not read the shortfall on its own - a `pending` grant refuses the request at a shortfall of 0, and the only thing to do about that is wait for `provisioning_deadline` and quote again.\n\nIt is a quote, not an admission ticket. The model, the appended duration, the variant count and the format are validated exactly as create validates them, so a rejection here is a rejection there. What it does **not** do is read the file your `input` points at - so it cannot know how long your source is, and every rule that depends on that length is checked only on the real call: a source under 3 seconds, and a result over 60. Nor is an `asset` id checked for ownership, for having landed, or against `max_bytes`, and a `url` is not fetched. A `200` here can still be followed by `audio_too_short`, `audio_too_long`, `audio_url_unreachable`, `audio_format_unsupported`, `asset_not_ready`, `payload_too_large`, `invalid_asset`, one of the matching `video_*` codes when you sent a clip, or `invalid_request` for a URL the fetcher refuses. Branch on `code` rather than on this list being complete. Nothing is charged either way.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cost, affordability and estimate.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Preflight"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/inpaint/edits": {
      "post": {
        "tags": [
          "Edit"
        ],
        "operationId": "createInpaint",
        "summary": "Replace a section of a clip",
        "description": "Replaces the part of your audio between `region.start_ms` and `region.end_ms`, and leaves the rest of the file exactly as it was. The new audio is generated from what surrounds the gap, so it lands in context instead of being dropped in.\n\nSend `input.video` to have the replacement follow what happens on screen, and a prompt to say what should be there instead of what is. You are billed on the width of the section you replace, never on the length of your file.\n\nWhere the section may start and how wide it may be are both per model, in `operations.inpaint` - read them from there. On `sfx-1.6` the opening second of a file cannot be replaced: it is the context the model conditions on.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to hold the request while the job runs, 1 to 25. Omit to return immediately. Out of range is rejected rather than clamped."
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^wait=(?:1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25)$"
            },
            "description": "`wait=<seconds>`, the RFC 7240 spelling of the `wait` query parameter, 1 to 25. If you send both, the query parameter wins."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "A unique string of your choosing.\n\nThe same key with the same body returns the original job instead of charging twice, and carries `Idempotent-Replayed: true`. It is also how you recover a job id if the connection drops while the server is holding your request. The same key with a *different* body is `409` - and bodies are compared **as you sent them**, so an extra or misspelled field counts as a difference even though the schemas themselves ignore unknown keys.\n\nFour things that are easy to assume the other way round:\n\n- **A key is scoped to one endpoint.** It is remembered per API key *and* per path, so the same key sent to two different collections creates two jobs and charges for both. Do not reuse one key across endpoints for what you think of as a single operation.\n- **A replay is re-rendered, not a stored response.** You get the job as it stands now, with fresh download links and a freshly read `Mirelo-Concurrency-Current`, so a create that answered `202` replays as `200` once the job has finished. The job is guaranteed to be the original one; the bytes of the response are not guaranteed to match.\n- **A failed create does not burn the key.** A create refused before the job runs, or one that fails while starting it - insufficient credits, an unusable asset, the concurrency ceiling - hands the key back, so nothing failed is ever stored or replayed and the same key is immediately safe to retry. The exception is a failure *after* the job is running, where the key stays held: retrying then gets the job, or a `503` asking you to try again in a moment while it is still being accepted.\n- **The window is a range, not exactly 24 hours.** A key is eligible to be forgotten 24 hours after the request that used it, and a daily sweep does the forgetting, so in practice it is remembered for 24 to 48 hours depending on when it was used. After that, reusing it starts new work and charges again."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InpaintRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Finished inside the wait window, or a replay of an earlier request with the same idempotency key that has already finished.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InpaintJob"
                }
              }
            }
          },
          "202": {
            "description": "Accepted and running, or a replay of an earlier request that has not finished yet. Poll `urls.self` after `Retry-After` seconds.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "Present and `true` when this response replays an earlier request with the same `Idempotency-Key` rather than fresh work. Absent otherwise.",
                "schema": {
                  "type": "boolean"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InpaintJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active subscription, or not enough credits. `error.credit_recovery` says how many credits the request needed, what the account can actually spend, and which billing step fixes it. Every refusal the credit check produces carries it, which today is all of them - handle its absence rather than assuming it, since a future 402 raised without reading the ledger would have no figures to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredError"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Either an idempotency key was reused with a different request body, or an asset in `input` is not usable yet - `code` says which. Reusing a key with the *same* body is not an error: it returns the original job.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "An uploaded asset is larger than the `max_bytes` that `POST /v3/assets` published. The upload policy refuses an oversized body at upload time, so reaching this means the object was staged some other way.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The file was fetched but cannot be used - the wrong kind of media for the field it was sent in, an asset that storage has but this endpoint cannot read, or a clip that does not carry what the request needs out of it, as a video with no audio track does not under `controls.preserve_speech`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/inpaint/edits/{id}": {
      "get": {
        "tags": [
          "Edit"
        ],
        "operationId": "getInpaint",
        "summary": "Fetch an inpaint edit",
        "description": "Returns the job in whatever state it is in. Keep fetching until `status` is final.\n\nAdd `?wait=25` to long-poll. A wait that runs out of time is not an error and not a different status code: you get `200` with a `status` that is still `queued` or `running`, plus `Retry-After` for the next attempt. Only create uses `202` to mean in progress.\n\nDownload links expire. Fetch the job again for fresh ones.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The job id. Take it from `urls.self` on the create response."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Seconds to wait for a final state before responding."
          }
        ],
        "responses": {
          "200": {
            "description": "The job, in whatever state it is in. A job older than its `expires_at` answers here too, with `status: \"expired\"` and no result, so an aged-out job stays distinguishable from an id that never existed. `Retry-After` is present while the job is still running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "How many of your jobs are running now, counting the one this response just accepted. Sent by the endpoints that start or track a generation, and by the `429` that refuses one for having too many already.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InpaintJob"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No job with that id. `param` is `\"id\"`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/inpaint/edits/preflight": {
      "post": {
        "tags": [
          "Edit"
        ],
        "operationId": "preflightInpaint",
        "summary": "Check cost and affordability before running",
        "description": "Send exactly the body you intend to send to the real endpoint and get back what it would cost, whether your account can fund it, and roughly how long it would take. Nothing is generated and nothing is charged.\n\n`credit_recovery` answers the funding question for **this** body, and a non-null `recovery_action` is the same step the `402` would have given you - so there is no need to spend a failed request to find out what to tell the customer. It is absent on a key that is not credit-metered, which is never refused for credits.\n\nThe request is affordable when all three hold: `provisioning_state` is `ready`, `recovery_action` is null, and `credit_shortfall` is 0. Do not read the shortfall on its own - a `pending` grant refuses the request at a shortfall of 0, and the only thing to do about that is wait for `provisioning_deadline` and quote again.\n\nIt is a quote, not an admission ticket. The model, the region, the variant count and the format are validated exactly as create validates them, so a rejection here is a rejection there. What it does **not** do is read the file your `input` points at - so it cannot know how long your source is, and the one region rule that depends on that length, `region.end_ms` past the end of your audio, is checked only on the real call. Nor is an `asset` id checked for ownership, for having landed, or against `max_bytes`, and a `url` is not fetched. A `200` here can still be followed by `invalid_region`, `audio_too_short`, `audio_too_long`, `audio_url_unreachable`, `audio_format_unsupported`, `asset_not_ready`, `payload_too_large`, `invalid_asset`, one of the matching `video_*` codes when you sent a clip, or `invalid_request` for a URL the fetcher refuses. Branch on `code` rather than on this list being complete. Nothing is charged either way.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InpaintRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cost, affordability and estimate.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Preflight"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed, asks for something this model does not serve, or names a model that does not serve this endpoint. `param` says which field.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited, or too many of your jobs are already running.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry - wait for `Retry-After`. `temporarily_unavailable` covers three things: a concurrent request with the same `Idempotency-Key` that has not finished being accepted yet - retry and you get its job - a deployment that cannot serve your key right now, and a check on your file we could not run this time, which is a statement about us rather than about the file.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "listModels",
        "summary": "List available models",
        "description": "Every model your key can use, which endpoint families accept it, and the limits for each. Read limits from here rather than hardcoding them. A family that is absent is not served; inside a family, an absent limit key means that parameter is not served either.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "responses": {
          "200": {
            "description": "The models.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/models/{model_id}": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "getModel",
        "summary": "Fetch one model",
        "description": "The same information as the list, for a single model.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          },
          {
            "name": "model_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sfx-1.6"
          }
        ],
        "responses": {
          "200": {
            "description": "The model.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such model. `param` is `\"model_id\"`.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/assets": {
      "post": {
        "tags": [
          "Assets"
        ],
        "operationId": "createAsset",
        "summary": "Get an upload link",
        "description": "Returns somewhere to upload your file to, and an id to use in later requests. You can skip this entirely and pass a public URL instead.\n\nThe upload is a `multipart/form-data` **POST**, not a PUT, and carries no `Authorization` header: send every entry of `fields` as a form field first, then your bytes as a final part named `file`. Storage ignores anything sent after the file part, so the order matters.\n\nIt goes straight to storage and we are not told when it lands, so **your upload’s own `2xx` is the signal that it finished** - wait for it before using the id. Generating against an id whose bytes are not there yet fails with `asset_not_ready`, which is retryable: the upload may still be in flight. That is a different error from `invalid_asset`, which means the file arrived and we could not read it.\n\nThe policy in `fields` pins `max_bytes`, the content type you asked for, and the key. An oversized or empty body is refused with a `400`; an edited field with a `403` naming the condition that failed. Nothing is stored either way.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAssetRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Where to upload.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetTicket"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v3/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getMe",
        "summary": "Your account",
        "description": "Which account this key belongs to, and whether the next request will be funded.\n\nTwo credit numbers, because they answer different questions: `credits_available` is the ledger balance, and `spend_capacity` is what a request may actually spend once jobs in flight and enabled overage are taken into account. Compare a preflight `credits` against `spend_capacity` to decide whether one particular request will be funded.\n\n`recovery_action` and `recovery_url` are resolved against the smallest billable request, so a non-null action here means the account cannot fund anything at all. For the step that applies to one particular body, preflight that body: it returns the same `credit_recovery` object sized to its own quote. This endpoint answers whether the account is stuck; preflight answers whether it can pay for one specific body.\n\nAn organization key is logged rather than debited, so it reports `unmetered` with all four credit fields null and no `email` - zero would read as out of credits for a key that is never charged, and there is no payer user to name.\n\nThis is also the one response that carries `Mirelo-Test-Account`, which says whether the account is ours rather than a customer's. Only useful if you report your own usage somewhere and want internal traffic labelled as such.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Mirelo-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "enum": [
                "2026-08-28"
              ]
            },
            "description": "Which API behaviour to serve. Omit it for current behaviour. A date we accept comes back on the response, so a log records the contract you were served under. A date this API never had is rejected rather than ignored, and that `400` carries the current date rather than the one you sent. There is one version today, so sending it selects no older shape: what it buys you is the change rule in **Versioning** in the overview."
          }
        ],
        "responses": {
          "200": {
            "description": "The account.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Mirelo-Test-Account": {
                "description": "Whether this account is one of ours rather than a customer's, so a client that reports its own usage can label it internal. Sent on `GET /v3/me` only, and there whatever the answer is - `false` is a real answer and comes back as `false`. Absent means we have none to give: an organization key carries the flag on no user, so read absent as unknown and leave whatever you had recorded alone.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key. `RateLimit-*` are absent when the key itself could not be read: authentication runs before the limiter, so there is no allowance to report.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your API key is valid but not allowed here. Either an organization key was used outside the pilot it is issued for, or the key has no user account to own what the request would create. `message` says which. Contact support rather than reissuing the key.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something broke on our side. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to take the request. Safe to retry.",
            "headers": {
              "Mirelo-Version": {
                "description": "The API behaviour this response was produced under.",
                "schema": {
                  "type": "string",
                  "format": "date"
                }
              },
              "Mirelo-Concurrency-Limit": {
                "description": "How many of your jobs may run at once. Set per key, so read it rather than assuming the default.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-trace-id": {
                "description": "This request's trace id. Quote it alongside `request_id` when you contact us. Absent on `GET .../{id}` for a generation, which is not traced per attempt.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Your **burst allowance**: the most requests you may spend at once, and what `RateLimit-Remaining` counts down from. It is deliberately larger than the rate you may sustain - size steady traffic off `RateLimit-Policy` instead, or you will plan for headroom that only exists once.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "How much of that burst allowance is left right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "**Seconds from now** until the allowance is back up to `RateLimit-Limit`, and `0` when it already is. A duration, not a timestamp - do not treat it as a unix time. It is not a window boundary either: the allowance refills continuously, so you can spend again long before this reaches `0`.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The sustained rate behind the burst, as `\"name\";q=<requests>;w=<seconds>` - ex: `\"v3-read\";q=600;w=60`. Divide `q` by `w` for the rate you may hold indefinitely. The name is the pool this request was counted against: reads, writes and generations have separate pools, so polling a job does not spend what creating one does.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Whole seconds to wait before trying again. Wait at least this long.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v2/audio-to-midi/v1.0/preflight": {
      "get": {
        "operationId": "audioToMidiV10Preflight",
        "summary": "Preflight",
        "description": "Apply max(1, round(seconds × 2.5)) to the caller-supplied duration_ms and return that credit estimate with the estimated processing time. This route does not download the audio, start a transcription, or deduct credits. The create routes bill the server-probed media duration, so the final charge can differ when duration_ms does not match the file.",
        "tags": [
          "Audio-to-MIDI Pro"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "duration_ms",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1800000
            },
            "description": "Caller-supplied estimate of the input duration in milliseconds (max 1,800,000). Priced as submitted, without downloading the audio; the create routes bill the server-probed duration instead."
          }
        ],
        "responses": {
          "200": {
            "description": "Credit cost and ETA",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "credits",
                    "billing_mode",
                    "credits_required",
                    "credits_available",
                    "credit_shortfall",
                    "recovery_action",
                    "recovery_url",
                    "provisioning_state",
                    "provisioning_deadline",
                    "estimated_ms"
                  ],
                  "properties": {
                    "credits": {
                      "type": "integer",
                      "description": "Exact credit cost — matches what will be deducted on success"
                    },
                    "billing_mode": {
                      "type": "string",
                      "enum": [
                        "metered",
                        "unmetered"
                      ],
                      "description": "unmetered organization requests are affordable without evaluating credit fields"
                    },
                    "credits_required": {
                      "type": "integer",
                      "description": "Exact credits required by the request"
                    },
                    "credits_available": {
                      "type": "integer",
                      "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending",
                      "nullable": true
                    },
                    "credit_shortfall": {
                      "type": "integer",
                      "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                    },
                    "recovery_action": {
                      "type": "string",
                      "enum": [
                        "enable_overage",
                        "increase_overage_limit",
                        "view_plans",
                        "contact_support"
                      ],
                      "nullable": true,
                      "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                    },
                    "recovery_url": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "Destination for recovery_action"
                    },
                    "provisioning_state": {
                      "type": "string",
                      "enum": [
                        "ready",
                        "pending",
                        "stalled"
                      ],
                      "nullable": true,
                      "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                    },
                    "provisioning_deadline": {
                      "type": "integer",
                      "nullable": true,
                      "description": "ms epoch when pending provisioning becomes stalled"
                    },
                    "estimated_ms": {
                      "type": "integer",
                      "description": "Estimated completion time in milliseconds (p90 calibrated)"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — missing or malformed parameters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status",
                        "credits_required",
                        "credits_available",
                        "credit_shortfall",
                        "recovery_action",
                        "recovery_url",
                        "provisioning_state",
                        "provisioning_deadline"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Audio exceeds the maximum allowed input length — error.code is audio_too_long",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — `error.code` is `rate_limited`. Either this API key exceeded its own request ceiling, an input URL's origin is throttling us (a short download link followed hard enough to be throttled does this), or an upstream model provider is throttling us. All three mean retry: the request and any URLs in it are still valid. `Retry-After` is present whenever the wait is known. Async jobs report the same code on the poll response.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Omitted when the wait is unknown.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/audio-to-midi/v1.0/sync": {
      "post": {
        "operationId": "audioToMidiV10Sync",
        "summary": "Transcribe audio to MIDI (sync)",
        "description": "Transcribe audio with Audio-to-MIDI Pro into MIDI, structured notes, and MusicXML. The v1.0 URL versions the API contract, not the model. The result’s model field identifies the producing Audio-to-MIDI release; retain it with your transcript. Input audio can be up to 30 minutes and 100 MiB. The byte limit is independent, so some uncompressed files reach it before the duration limit; use a compressed format for long sources. Sync has a separate 12-minute limit based on the probed duration. Longer inputs return 422 sync_too_long before transcription or billing; use POST /v2/audio-to-midi/v1.0/jobs instead. After success the server bills max(1, round(seconds × 2.5)) over the duration it probes from the downloaded media, so the charge can differ from a preflight quote based on an inaccurate duration_ms. This request blocks until the filtered final result is ready and must finish within one server action; a longer client timeout does not extend the sync server runtime. Send one stable Idempotency-Key per logical sync request so a timed-out call can be retried safely.",
        "tags": [
          "Audio-to-MIDI Pro"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Stable key for one logical sync request, for retrying after an ambiguous response such as a transport timeout. Reuse it only with the identical body. While the original request is in progress, after it completed, or with a different body, the retry returns 409 idempotency_conflict instead of starting another billable request. A failed attempt may run again with the same key. A completed request's key is remembered for 24 to 48 hours after the original request completes, depending on when the daily sweep runs. A 409 retry does not extend this window; reusing the key after it is forgotten starts and bills a new request.",
            "example": "order-123"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioToMidiRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transcription artifacts and metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "midi_url",
                    "notes",
                    "duration_seconds",
                    "note_count",
                    "timing",
                    "model"
                  ],
                  "properties": {
                    "model": {
                      "type": "string",
                      "nullable": true,
                      "description": "Public release that produced this result. Null when unknown or when multiple releases contributed; not a runtime fingerprint or reproducibility guarantee."
                    },
                    "midi_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Presigned URL to the generated MIDI file"
                    },
                    "musicxml_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Presigned URL to the generated MusicXML file. Omitted when transcription produced no notes."
                    },
                    "musicxml_optimized": {
                      "type": "boolean",
                      "description": "Present with `musicxml_url`. True when the file is the optimized score requested with `optimize_musicxml`; false when optimization was not requested or the standard score was delivered instead."
                    },
                    "score_bundle_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Presigned URL to an immutable ZIP containing the full score, part PDFs, and eligible tablature PDFs. Present only when `score_pdfs` was requested and rendering succeeded."
                    },
                    "score_manifest_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Presigned URL to the export bundle's hash and tuning manifest."
                    },
                    "score_pdfs": {
                      "type": "array",
                      "description": "Individually downloadable engraved PDFs. Every sounded part has a standard part PDF; eligible fretted parts also have tablature.",
                      "items": {
                        "type": "object",
                        "required": [
                          "kind",
                          "filename",
                          "url",
                          "sha256",
                          "size_bytes"
                        ],
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "full_score",
                              "part",
                              "tablature"
                            ]
                          },
                          "filename": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "sha256": {
                            "type": "string"
                          },
                          "size_bytes": {
                            "type": "integer"
                          },
                          "part_id": {
                            "type": "string"
                          },
                          "part_name": {
                            "type": "string"
                          },
                          "tuning": {
                            "type": "object",
                            "required": [
                              "source",
                              "pitches"
                            ],
                            "properties": {
                              "source": {
                                "type": "string",
                                "enum": [
                                  "encoded",
                                  "musescore_default"
                                ],
                                "description": "Whether the tuning came from the score or MuseScore's instrument definition."
                              },
                              "pitches": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "description": "Open-string sounding pitches in scientific pitch notation, lowest string first."
                              }
                            }
                          }
                        }
                      }
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "pitch",
                          "start",
                          "end",
                          "instrument",
                          "velocity"
                        ],
                        "properties": {
                          "pitch": {
                            "type": "number",
                            "description": "MIDI note number"
                          },
                          "start": {
                            "type": "number",
                            "description": "Start time in seconds"
                          },
                          "end": {
                            "type": "number",
                            "description": "End time in seconds"
                          },
                          "instrument": {
                            "type": "string",
                            "description": "Detected or assigned instrument label"
                          },
                          "velocity": {
                            "type": "number",
                            "description": "Note velocity (0–127)"
                          }
                        }
                      },
                      "description": "Complete authoritative note list after terminal filtering"
                    },
                    "duration_seconds": {
                      "type": "number",
                      "description": "Duration of the input audio in seconds"
                    },
                    "note_count": {
                      "type": "integer",
                      "description": "Number of transcribed notes"
                    },
                    "tempo_bpm": {
                      "type": "number",
                      "description": "Estimated tempo in BPM when beat-grid detection succeeds"
                    },
                    "timing": {
                      "type": "object",
                      "description": "What the export did about time. `applied` is what the files carry; it differs from `requested` only when a `quantized` request met a beat grid too unreliable to move notes onto.",
                      "required": [
                        "requested",
                        "applied",
                        "subdivision"
                      ],
                      "properties": {
                        "requested": {
                          "type": "string",
                          "enum": [
                            "performance",
                            "quantized"
                          ]
                        },
                        "applied": {
                          "type": "string",
                          "enum": [
                            "performance",
                            "quantized"
                          ]
                        },
                        "fallback_reason": {
                          "type": "string",
                          "enum": [
                            "grid_unavailable",
                            "too_few_beats",
                            "low_coverage",
                            "unstable_pacing",
                            "unstable_intervals",
                            "degenerate_downbeats",
                            "implausible_tempo"
                          ],
                          "description": "Why the request was not honoured. Present only when `applied` differs from `requested`. `grid_unavailable`: Beat detection produced no grid for this audio. `too_few_beats`: Too few beats were detected to define a grid. `low_coverage`: The detected beats cover too little of the audio. `unstable_pacing`: The detected beats are spaced too unevenly to describe the performance. `unstable_intervals`: The detected beats are steady enough to describe the performance, but not steady enough to move notes onto. `degenerate_downbeats`: Nearly every beat was marked as a downbeat, so no metre was found. `implausible_tempo`: The detected beats imply a tempo outside the range a reader accepts."
                        },
                        "preserved_articulations": {
                          "type": "object",
                          "description": "Where quantized timing could not put every note on the 1/16 grid without losing one. A drum roll or a flam lives inside a single 16th, so snapping it to 16ths would put every hit on one position and the file could carry only one of them; those hits are written on a finer subdivision instead, or left at their played time when even the finest one runs out. Present only when that happened — its absence from a `quantized` response means every note sits on the requested subdivision.",
                          "required": [
                            "refined_note_count",
                            "performance_note_count",
                            "finest_subdivisions_per_beat"
                          ],
                          "properties": {
                            "refined_note_count": {
                              "type": "number",
                              "description": "Notes written on a finer subdivision than 1/16 so a close repeat stayed distinct"
                            },
                            "performance_note_count": {
                              "type": "number",
                              "description": "Notes left at their played time because no subdivision kept them distinct"
                            },
                            "finest_subdivisions_per_beat": {
                              "type": "number",
                              "description": "Finest subdivisions of a beat the files state: 4 is a 16th, 12 a 32nd triplet, 32 a 128th"
                            }
                          }
                        },
                        "subdivision": {
                          "type": "object",
                          "description": "The rhythmic grid shared by MIDI and MusicXML. Automatic selection can use multiple stable regions and reports when sparse or ambiguous evidence kept the straight default.",
                          "required": [
                            "version",
                            "requested",
                            "applied",
                            "confidence",
                            "regions"
                          ],
                          "properties": {
                            "version": {
                              "type": "integer"
                            },
                            "requested": {
                              "type": "string",
                              "enum": [
                                "automatic",
                                "straight_sixteenths",
                                "eighth_triplets",
                                "sixteenth_triplets",
                                "swing_eighths"
                              ]
                            },
                            "applied": {
                              "type": "string",
                              "enum": [
                                "straight_sixteenths",
                                "eighth_triplets",
                                "sixteenth_triplets",
                                "swing_eighths"
                              ]
                            },
                            "confidence": {
                              "type": "number",
                              "minimum": 0,
                              "maximum": 1
                            },
                            "refusal": {
                              "type": "string",
                              "enum": [
                                "ambiguous",
                                "sparse"
                              ]
                            },
                            "regions": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "required": [
                                  "start_quarter",
                                  "subdivision",
                                  "confidence"
                                ],
                                "properties": {
                                  "start_quarter": {
                                    "type": "number"
                                  },
                                  "end_quarter": {
                                    "type": "number"
                                  },
                                  "subdivision": {
                                    "type": "string",
                                    "enum": [
                                      "straight_sixteenths",
                                      "eighth_triplets",
                                      "sixteenth_triplets",
                                      "swing_eighths"
                                    ]
                                  },
                                  "confidence": {
                                    "type": "number",
                                    "minimum": 0,
                                    "maximum": 1
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "time_grid": {
                      "type": "object",
                      "description": "The bar grid the exported files state. Reported so a consumer can reconstruct bar lines instead of inferring them from `tempo_bpm`.",
                      "required": [
                        "time_signature",
                        "score_quarters_per_detected_beat",
                        "pickup_quarters",
                        "beats_per_bar",
                        "meter_source",
                        "pickup_beats",
                        "tempo_bpm",
                        "tempo_source",
                        "tempo_curve"
                      ],
                      "properties": {
                        "time_signature": {
                          "type": "object",
                          "required": [
                            "numerator",
                            "denominator"
                          ],
                          "properties": {
                            "numerator": {
                              "type": "integer"
                            },
                            "denominator": {
                              "type": "integer",
                              "enum": [
                                4,
                                8
                              ]
                            }
                          }
                        },
                        "score_quarters_per_detected_beat": {
                          "type": "number",
                          "description": "Duration of one detected pulse in score quarter notes; 1.5 for compound eighth-note meters."
                        },
                        "pickup_quarters": {
                          "type": "number",
                          "description": "Anacrusis duration in score quarter notes."
                        },
                        "beats_per_bar": {
                          "type": "number",
                          "description": "Beats in a bar — the metre's numerator"
                        },
                        "meter_source": {
                          "type": "string",
                          "enum": [
                            "detected",
                            "detected_weak",
                            "caller",
                            "default"
                          ],
                          "description": "`detected` when the downbeats agreed on the metre, `detected_weak` when they landed on it without agreeing, `caller` for an explicit signature, and `default` when no bar length was measured and the 4/4 fallback was written."
                        },
                        "pickup_beats": {
                          "type": "number",
                          "description": "Musical beats before the first bar line (anacrusis). Silent recording preroll is excluded and reported by `score_origin_seconds` instead. A non-zero pickup shifts every bar line after it."
                        },
                        "score_origin_seconds": {
                          "type": "number",
                          "description": "Absolute source-audio time represented by score beat zero. 0 for a true pickup or audio that begins on bar one; positive when silent recording preroll was removed from the score. Omitted only on stored async results created before this field shipped."
                        },
                        "tempo_bpm": {
                          "type": "number",
                          "description": "The tempo the files state. Always present — unlike the top-level `tempo_bpm`, which is omitted when no tempo was detected."
                        },
                        "tempo_source": {
                          "type": "string",
                          "enum": [
                            "detected",
                            "caller",
                            "default"
                          ],
                          "description": "Where `tempo_bpm` came from: the detected beats, a caller-stated BPM, or the export default when no usable tempo was detected."
                        },
                        "tempo_curve": {
                          "type": "boolean",
                          "description": "True when the files carry a tempo map following the performance; false when they state one constant tempo."
                        }
                      }
                    },
                    "instrument_detection_credits_returned": {
                      "type": "integer",
                      "description": "Present when the request's `instrument_detection_id` was a charged detection: the credits that came off this transcription's charge."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — missing or malformed parameters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status",
                        "credits_required",
                        "credits_available",
                        "credit_shortfall",
                        "recovery_action",
                        "recovery_url",
                        "provisioning_state",
                        "provisioning_deadline"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key belongs to an in-progress or completed sync request, or was reused with a different body — error.code is idempotency_conflict. Nothing new is started or billed, and the original response is not replayed. This 409 does not extend the key's retention window. A key whose earlier attempt failed may run again instead of returning this response, as may one the daily sweep has already forgotten (24 to 48 hours after the original request completes).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy",
                          "enum": [
                            "idempotency_conflict"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Audio exceeds the product limit (audio_too_long) or the sync execution limit (sync_too_long). For sync_too_long, use POST /v2/audio-to-midi/v1.0/jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — `error.code` is `rate_limited`. Either this API key exceeded its own request ceiling, an input URL's origin is throttling us (a short download link followed hard enough to be throttled does this), or an upstream model provider is throttling us. All three mean retry: the request and any URLs in it are still valid. `Retry-After` is present whenever the wait is known. Async jobs report the same code on the poll response.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Omitted when the wait is unknown.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/audio-to-midi/v1.0/jobs": {
      "post": {
        "operationId": "audioToMidiV10JobCreate",
        "summary": "Start async transcription",
        "description": "Start an Audio-to-MIDI Pro job and return immediately. The v1.0 URL versions the API contract, not the model. The result’s model field identifies the producing Audio-to-MIDI release; retain it with your transcript. Input audio can be up to 30 minutes and 100 MiB. The byte limit is independent, so some uncompressed files reach it before the duration limit; use a compressed format for long sources. After success the server bills max(1, round(seconds × 2.5)) over the duration it probes from the downloaded media, so the charge can differ from a preflight quote based on an inaccurate duration_ms. A key can have at most 5 Audio-to-MIDI jobs in flight by default, independently of its v3 generation jobs; approved keys may have a higher configured ceiling. Poll the returned `job_url` until `status` is `succeeded` or `errored`. Prefer this route for long files, batches, serverless functions, and high-volume integrations. Idempotency-Key is not supported here; resubmitting the create request can start and bill a separate job.",
        "tags": [
          "Audio-to-MIDI Pro"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioToMidiRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Async job accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_id",
                    "job_url",
                    "estimated_ms",
                    "estimated_completion_at"
                  ],
                  "properties": {
                    "job_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unique identifier for the async job"
                    },
                    "job_url": {
                      "type": "string",
                      "description": "Relative URL to poll for job status, e.g. `/v2/text-to-sfx/{version}/jobs/{job_id}`. Append to your API base URL to form the full polling URL."
                    },
                    "estimated_ms": {
                      "type": "integer",
                      "description": "Estimated completion time in milliseconds"
                    },
                    "estimated_completion_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 estimated completion timestamp"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — missing or malformed parameters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status",
                        "credits_required",
                        "credits_available",
                        "credit_shortfall",
                        "recovery_action",
                        "recovery_url",
                        "provisioning_state",
                        "provisioning_deadline"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Audio exceeds the maximum allowed input length — error.code is audio_too_long",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited or the Audio-to-MIDI concurrency ceiling was reached. A concurrency refusal uses `error.code = concurrency_limit_reached`; wait for a running job to finish before retrying.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Omitted when the wait is unknown.",
                "schema": {
                  "type": "integer"
                }
              },
              "Mirelo-Concurrency-Current": {
                "description": "In-flight jobs counted when this create was refused. Present for concurrency refusals.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/audio-to-midi/v1.0/instruments": {
      "post": {
        "operationId": "audioToMidiV10Instruments",
        "summary": "Suggest instruments",
        "description": "Suggests instruments for the `instruments` field of an Audio-to-MIDI request. The detector listens to sampled windows of the audio, reported in `windows`, several times each. Suggestions need review: `instruments` is exhaustive, so a missing instrument cannot appear in the transcription and a wrong one pulls notes onto the wrong part. Review every suggestion, not only the low-agreement ones, and add anything the detector missed; a missing instrument carries no score at all. Upload the audio once with POST /v2/assets and pass the same asset to this route and to the transcription; for a URL source the response returns the stored copy's `asset_id`. A detection is free when an Audio-to-MIDI request that succeeds passes its `instrument_detection_id`. Unused detections are free up to 10 per account in any 24 hours. Past that a detection costs up to 50 credits (never more than transcribing the same audio), charged on success, and those credits come off the transcription that later uses it within 30 days, in the same billing period. Organization keys cannot call this route yet. Send `max_credits: 0` to run only when free: past the allowance the request fails with `max_credits_exceeded` and charges nothing. The same account, audio content and detector revision returns the cached answer free.",
        "tags": [
          "Audio-to-MIDI Pro"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioToMidiInstrumentsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suggested instruments for review",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "instrument_detection_id",
                    "detector_revision",
                    "asset_id",
                    "recommended_instruments",
                    "instruments",
                    "windows",
                    "credits_charged",
                    "free_detections_remaining"
                  ],
                  "properties": {
                    "instrument_detection_id": {
                      "type": "string",
                      "description": "Pass as `instrument_detection_id` on the Audio-to-MIDI request that uses these suggestions."
                    },
                    "detector_revision": {
                      "type": "string",
                      "description": "Identifies the detector that produced this answer. A cached answer is reused only for the same revision."
                    },
                    "asset_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The asset that was analysed. For a URL source this is the stored copy; pass it as `audio` to the Audio-to-MIDI request."
                    },
                    "recommended_instruments": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The instruments the detector recommends, ready to pass as `instruments` after you review them."
                    },
                    "instruments": {
                      "type": "array",
                      "description": "Every instrument the detector admitted, recommended ones first.",
                      "items": {
                        "type": "object",
                        "required": [
                          "name",
                          "recommended",
                          "agreement",
                          "windows"
                        ],
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "recommended": {
                            "type": "boolean"
                          },
                          "agreement": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 1,
                            "description": "The largest share of one window's independent listens that named this instrument. A rough signal of how often a label is right, not a probability."
                          },
                          "windows": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Indexes into `windows` of the sampled windows it was heard in."
                          }
                        }
                      }
                    },
                    "windows": {
                      "type": "array",
                      "description": "The windows of the audio the detector listened to.",
                      "items": {
                        "type": "object",
                        "required": [
                          "start_ms",
                          "end_ms"
                        ],
                        "properties": {
                          "start_ms": {
                            "type": "integer"
                          },
                          "end_ms": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "credits_charged": {
                      "type": "integer",
                      "description": "Credits this request charged. Zero when the detection was free or cached."
                    },
                    "free_detections_remaining": {
                      "type": "integer",
                      "description": "Free unused detections left in the current window."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — missing or malformed parameters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits, or `max_credits_exceeded` when the detection would cost more than `max_credits`. A `max_credits_exceeded` error also carries `credits_required`, `max_credits` and `free_detections_remaining`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status",
                        "credits_required",
                        "credits_available",
                        "credit_shortfall",
                        "recovery_action",
                        "recovery_url",
                        "provisioning_state",
                        "provisioning_deadline"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "A detection of the same audio is already running for this account; retry shortly. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Audio exceeds the maximum allowed input length — error.code is audio_too_long",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — `error.code` is `rate_limited`. Either this API key exceeded its own request ceiling, an input URL's origin is throttling us (a short download link followed hard enough to be throttled does this), or an upstream model provider is throttling us. All three mean retry: the request and any URLs in it are still valid. `Retry-After` is present whenever the wait is known. Async jobs report the same code on the poll response.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Omitted when the wait is unknown.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Detection could not complete; retry shortly. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/audio-to-midi/v1.0/jobs/{job_id}": {
      "get": {
        "operationId": "audioToMidiV10JobPoll",
        "summary": "Poll async transcription",
        "description": "Poll the returned `job_url` until `status` is `succeeded` or `errored`. While processing, replace your local preview with `notes`. When the job succeeds, replace it with `result.notes`, the complete filtered result. No additional request or filtering pass is required.",
        "tags": [
          "Audio-to-MIDI Pro"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Job ID returned from the job creation endpoint"
          }
        ],
        "responses": {
          "200": {
            "description": "Current provisional or terminal job snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "title": "Processing",
                      "type": "object",
                      "required": [
                        "job_id",
                        "status",
                        "created_at",
                        "estimated_completion_at",
                        "estimated_ms",
                        "progress_percent",
                        "notes",
                        "note_count",
                        "request"
                      ],
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "processing"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "estimated_completion_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "estimated_ms": {
                          "type": "integer"
                        },
                        "progress_percent": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 96
                        },
                        "notes": {
                          "type": "array",
                          "description": "Current provisional closed-note snapshot. Replace the previous `processing.notes` value with this array on every poll. It normally grows, but can restart if processing is retried. When `status` is `succeeded`, replace it with `result.notes`, the complete filtered result. Times remain in the original performance grid until final timing is applied.",
                          "items": {
                            "type": "object",
                            "required": [
                              "pitch",
                              "start",
                              "end",
                              "instrument",
                              "velocity"
                            ],
                            "properties": {
                              "pitch": {
                                "type": "number",
                                "description": "MIDI note number"
                              },
                              "start": {
                                "type": "number",
                                "description": "Start time in seconds"
                              },
                              "end": {
                                "type": "number",
                                "description": "End time in seconds"
                              },
                              "instrument": {
                                "type": "string",
                                "description": "Detected or assigned instrument label"
                              },
                              "velocity": {
                                "type": "number",
                                "description": "Note velocity (0–127)"
                              }
                            }
                          }
                        },
                        "note_count": {
                          "type": "integer",
                          "description": "Number of closed notes in `notes` so far"
                        },
                        "request": {
                          "type": "object"
                        }
                      }
                    },
                    {
                      "title": "Succeeded",
                      "type": "object",
                      "required": [
                        "job_id",
                        "status",
                        "created_at",
                        "completed_at",
                        "result",
                        "request"
                      ],
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "succeeded"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "completed_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "result": {
                          "type": "object",
                          "required": [
                            "midi_url",
                            "notes",
                            "duration_seconds",
                            "note_count",
                            "timing",
                            "model"
                          ],
                          "properties": {
                            "model": {
                              "type": "string",
                              "nullable": true,
                              "description": "Public release that produced this result. Null when unknown or when multiple releases contributed; not a runtime fingerprint or reproducibility guarantee."
                            },
                            "midi_url": {
                              "type": "string",
                              "format": "uri",
                              "description": "Presigned URL to the generated MIDI file"
                            },
                            "musicxml_url": {
                              "type": "string",
                              "format": "uri",
                              "description": "Presigned URL to the generated MusicXML file. Omitted when transcription produced no notes."
                            },
                            "musicxml_optimized": {
                              "type": "boolean",
                              "description": "Present with `musicxml_url`. True when the file is the optimized score requested with `optimize_musicxml`; false when optimization was not requested or the standard score was delivered instead."
                            },
                            "score_bundle_url": {
                              "type": "string",
                              "format": "uri",
                              "description": "Presigned URL to an immutable ZIP containing the full score, part PDFs, and eligible tablature PDFs. Present only when `score_pdfs` was requested and rendering succeeded."
                            },
                            "score_manifest_url": {
                              "type": "string",
                              "format": "uri",
                              "description": "Presigned URL to the export bundle's hash and tuning manifest."
                            },
                            "score_pdfs": {
                              "type": "array",
                              "description": "Individually downloadable engraved PDFs. Every sounded part has a standard part PDF; eligible fretted parts also have tablature.",
                              "items": {
                                "type": "object",
                                "required": [
                                  "kind",
                                  "filename",
                                  "url",
                                  "sha256",
                                  "size_bytes"
                                ],
                                "properties": {
                                  "kind": {
                                    "type": "string",
                                    "enum": [
                                      "full_score",
                                      "part",
                                      "tablature"
                                    ]
                                  },
                                  "filename": {
                                    "type": "string"
                                  },
                                  "url": {
                                    "type": "string",
                                    "format": "uri"
                                  },
                                  "sha256": {
                                    "type": "string"
                                  },
                                  "size_bytes": {
                                    "type": "integer"
                                  },
                                  "part_id": {
                                    "type": "string"
                                  },
                                  "part_name": {
                                    "type": "string"
                                  },
                                  "tuning": {
                                    "type": "object",
                                    "required": [
                                      "source",
                                      "pitches"
                                    ],
                                    "properties": {
                                      "source": {
                                        "type": "string",
                                        "enum": [
                                          "encoded",
                                          "musescore_default"
                                        ],
                                        "description": "Whether the tuning came from the score or MuseScore's instrument definition."
                                      },
                                      "pitches": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Open-string sounding pitches in scientific pitch notation, lowest string first."
                                      }
                                    }
                                  }
                                }
                              }
                            },
                            "notes": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "required": [
                                  "pitch",
                                  "start",
                                  "end",
                                  "instrument",
                                  "velocity"
                                ],
                                "properties": {
                                  "pitch": {
                                    "type": "number",
                                    "description": "MIDI note number"
                                  },
                                  "start": {
                                    "type": "number",
                                    "description": "Start time in seconds"
                                  },
                                  "end": {
                                    "type": "number",
                                    "description": "End time in seconds"
                                  },
                                  "instrument": {
                                    "type": "string",
                                    "description": "Detected or assigned instrument label"
                                  },
                                  "velocity": {
                                    "type": "number",
                                    "description": "Note velocity (0–127)"
                                  }
                                }
                              },
                              "description": "Complete authoritative note list after terminal filtering"
                            },
                            "duration_seconds": {
                              "type": "number",
                              "description": "Duration of the input audio in seconds"
                            },
                            "note_count": {
                              "type": "integer",
                              "description": "Number of transcribed notes"
                            },
                            "tempo_bpm": {
                              "type": "number",
                              "description": "Estimated tempo in BPM when beat-grid detection succeeds"
                            },
                            "timing": {
                              "type": "object",
                              "description": "What the export did about time. `applied` is what the files carry; it differs from `requested` only when a `quantized` request met a beat grid too unreliable to move notes onto.",
                              "required": [
                                "requested",
                                "applied",
                                "subdivision"
                              ],
                              "properties": {
                                "requested": {
                                  "type": "string",
                                  "enum": [
                                    "performance",
                                    "quantized"
                                  ]
                                },
                                "applied": {
                                  "type": "string",
                                  "enum": [
                                    "performance",
                                    "quantized"
                                  ]
                                },
                                "fallback_reason": {
                                  "type": "string",
                                  "enum": [
                                    "grid_unavailable",
                                    "too_few_beats",
                                    "low_coverage",
                                    "unstable_pacing",
                                    "unstable_intervals",
                                    "degenerate_downbeats",
                                    "implausible_tempo"
                                  ],
                                  "description": "Why the request was not honoured. Present only when `applied` differs from `requested`. `grid_unavailable`: Beat detection produced no grid for this audio. `too_few_beats`: Too few beats were detected to define a grid. `low_coverage`: The detected beats cover too little of the audio. `unstable_pacing`: The detected beats are spaced too unevenly to describe the performance. `unstable_intervals`: The detected beats are steady enough to describe the performance, but not steady enough to move notes onto. `degenerate_downbeats`: Nearly every beat was marked as a downbeat, so no metre was found. `implausible_tempo`: The detected beats imply a tempo outside the range a reader accepts."
                                },
                                "preserved_articulations": {
                                  "type": "object",
                                  "description": "Where quantized timing could not put every note on the 1/16 grid without losing one. A drum roll or a flam lives inside a single 16th, so snapping it to 16ths would put every hit on one position and the file could carry only one of them; those hits are written on a finer subdivision instead, or left at their played time when even the finest one runs out. Present only when that happened — its absence from a `quantized` response means every note sits on the requested subdivision.",
                                  "required": [
                                    "refined_note_count",
                                    "performance_note_count",
                                    "finest_subdivisions_per_beat"
                                  ],
                                  "properties": {
                                    "refined_note_count": {
                                      "type": "number",
                                      "description": "Notes written on a finer subdivision than 1/16 so a close repeat stayed distinct"
                                    },
                                    "performance_note_count": {
                                      "type": "number",
                                      "description": "Notes left at their played time because no subdivision kept them distinct"
                                    },
                                    "finest_subdivisions_per_beat": {
                                      "type": "number",
                                      "description": "Finest subdivisions of a beat the files state: 4 is a 16th, 12 a 32nd triplet, 32 a 128th"
                                    }
                                  }
                                },
                                "subdivision": {
                                  "type": "object",
                                  "description": "The rhythmic grid shared by MIDI and MusicXML. Automatic selection can use multiple stable regions and reports when sparse or ambiguous evidence kept the straight default.",
                                  "required": [
                                    "version",
                                    "requested",
                                    "applied",
                                    "confidence",
                                    "regions"
                                  ],
                                  "properties": {
                                    "version": {
                                      "type": "integer"
                                    },
                                    "requested": {
                                      "type": "string",
                                      "enum": [
                                        "automatic",
                                        "straight_sixteenths",
                                        "eighth_triplets",
                                        "sixteenth_triplets",
                                        "swing_eighths"
                                      ]
                                    },
                                    "applied": {
                                      "type": "string",
                                      "enum": [
                                        "straight_sixteenths",
                                        "eighth_triplets",
                                        "sixteenth_triplets",
                                        "swing_eighths"
                                      ]
                                    },
                                    "confidence": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 1
                                    },
                                    "refusal": {
                                      "type": "string",
                                      "enum": [
                                        "ambiguous",
                                        "sparse"
                                      ]
                                    },
                                    "regions": {
                                      "type": "array",
                                      "items": {
                                        "type": "object",
                                        "required": [
                                          "start_quarter",
                                          "subdivision",
                                          "confidence"
                                        ],
                                        "properties": {
                                          "start_quarter": {
                                            "type": "number"
                                          },
                                          "end_quarter": {
                                            "type": "number"
                                          },
                                          "subdivision": {
                                            "type": "string",
                                            "enum": [
                                              "straight_sixteenths",
                                              "eighth_triplets",
                                              "sixteenth_triplets",
                                              "swing_eighths"
                                            ]
                                          },
                                          "confidence": {
                                            "type": "number",
                                            "minimum": 0,
                                            "maximum": 1
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            },
                            "time_grid": {
                              "type": "object",
                              "description": "The bar grid the exported files state. Reported so a consumer can reconstruct bar lines instead of inferring them from `tempo_bpm`.",
                              "required": [
                                "time_signature",
                                "score_quarters_per_detected_beat",
                                "pickup_quarters",
                                "beats_per_bar",
                                "meter_source",
                                "pickup_beats",
                                "tempo_bpm",
                                "tempo_source",
                                "tempo_curve"
                              ],
                              "properties": {
                                "time_signature": {
                                  "type": "object",
                                  "required": [
                                    "numerator",
                                    "denominator"
                                  ],
                                  "properties": {
                                    "numerator": {
                                      "type": "integer"
                                    },
                                    "denominator": {
                                      "type": "integer",
                                      "enum": [
                                        4,
                                        8
                                      ]
                                    }
                                  }
                                },
                                "score_quarters_per_detected_beat": {
                                  "type": "number",
                                  "description": "Duration of one detected pulse in score quarter notes; 1.5 for compound eighth-note meters."
                                },
                                "pickup_quarters": {
                                  "type": "number",
                                  "description": "Anacrusis duration in score quarter notes."
                                },
                                "beats_per_bar": {
                                  "type": "number",
                                  "description": "Beats in a bar — the metre's numerator"
                                },
                                "meter_source": {
                                  "type": "string",
                                  "enum": [
                                    "detected",
                                    "detected_weak",
                                    "caller",
                                    "default"
                                  ],
                                  "description": "`detected` when the downbeats agreed on the metre, `detected_weak` when they landed on it without agreeing, `caller` for an explicit signature, and `default` when no bar length was measured and the 4/4 fallback was written."
                                },
                                "pickup_beats": {
                                  "type": "number",
                                  "description": "Musical beats before the first bar line (anacrusis). Silent recording preroll is excluded and reported by `score_origin_seconds` instead. A non-zero pickup shifts every bar line after it."
                                },
                                "score_origin_seconds": {
                                  "type": "number",
                                  "description": "Absolute source-audio time represented by score beat zero. 0 for a true pickup or audio that begins on bar one; positive when silent recording preroll was removed from the score. Omitted only on stored async results created before this field shipped."
                                },
                                "tempo_bpm": {
                                  "type": "number",
                                  "description": "The tempo the files state. Always present — unlike the top-level `tempo_bpm`, which is omitted when no tempo was detected."
                                },
                                "tempo_source": {
                                  "type": "string",
                                  "enum": [
                                    "detected",
                                    "caller",
                                    "default"
                                  ],
                                  "description": "Where `tempo_bpm` came from: the detected beats, a caller-stated BPM, or the export default when no usable tempo was detected."
                                },
                                "tempo_curve": {
                                  "type": "boolean",
                                  "description": "True when the files carry a tempo map following the performance; false when they state one constant tempo."
                                }
                              }
                            },
                            "instrument_detection_credits_returned": {
                              "type": "integer",
                              "description": "Present when the request's `instrument_detection_id` was a charged detection: the credits that came off this transcription's charge."
                            }
                          }
                        },
                        "request": {
                          "type": "object"
                        }
                      }
                    },
                    {
                      "title": "Errored",
                      "type": "object",
                      "required": [
                        "job_id",
                        "status",
                        "created_at",
                        "error",
                        "request"
                      ],
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "errored"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "completed_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "error": {
                          "type": "object",
                          "required": [
                            "code",
                            "message",
                            "http_status"
                          ],
                          "properties": {
                            "code": {
                              "type": "string"
                            },
                            "message": {
                              "type": "string"
                            },
                            "http_status": {
                              "type": "integer"
                            },
                            "credits_required": {
                              "type": "integer",
                              "description": "Exact credits required by the request"
                            },
                            "credits_available": {
                              "type": "integer",
                              "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                            },
                            "credit_shortfall": {
                              "type": "integer",
                              "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                            },
                            "recovery_action": {
                              "type": "string",
                              "enum": [
                                "enable_overage",
                                "increase_overage_limit",
                                "view_plans",
                                "contact_support"
                              ],
                              "nullable": true,
                              "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                            },
                            "recovery_url": {
                              "type": "string",
                              "format": "uri",
                              "nullable": true,
                              "description": "Destination for recovery_action"
                            },
                            "provisioning_state": {
                              "type": "string",
                              "enum": [
                                "ready",
                                "pending",
                                "stalled"
                              ],
                              "nullable": true,
                              "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                            },
                            "provisioning_deadline": {
                              "type": "integer",
                              "nullable": true,
                              "description": "ms epoch when pending provisioning becomes stalled"
                            }
                          }
                        },
                        "request": {
                          "type": "object"
                        }
                      }
                    }
                  ],
                  "discriminator": {
                    "propertyName": "status"
                  }
                },
                "examples": {
                  "processing": {
                    "summary": "Current provisional transcription snapshot",
                    "value": {
                      "job_id": "550e8400-e29b-41d4-a716-446655440002",
                      "status": "processing",
                      "created_at": "2024-01-15T10:30:00.000Z",
                      "estimated_completion_at": "2024-01-15T10:30:45.000Z",
                      "estimated_ms": 45000,
                      "progress_percent": 32,
                      "notes": [
                        {
                          "pitch": 60,
                          "start": 0,
                          "end": 0.5,
                          "instrument": "piano",
                          "velocity": 80
                        },
                        {
                          "pitch": 61,
                          "start": 0.6,
                          "end": 0.9,
                          "instrument": "piano",
                          "velocity": 72
                        }
                      ],
                      "note_count": 2,
                      "request": {
                        "audio": {
                          "type": "url",
                          "audio_url": "https://example.com/song.mp3"
                        },
                        "timing": "quantized",
                        "tempo": {
                          "mode": "fixed",
                          "bpm": 120
                        },
                        "optimize_musicxml": true,
                        "score_pdfs": true,
                        "page_size": "letter"
                      }
                    }
                  },
                  "succeeded": {
                    "summary": "Authoritative filtered transcription result",
                    "value": {
                      "job_id": "550e8400-e29b-41d4-a716-446655440002",
                      "status": "succeeded",
                      "created_at": "2024-01-15T10:30:00.000Z",
                      "completed_at": "2024-01-15T10:30:42.120Z",
                      "result": {
                        "model": "a2m-1.1",
                        "midi_url": "https://cdn.mirelo.ai/api/audio-to-midi/abc/transcription.mid",
                        "musicxml_url": "https://cdn.mirelo.ai/api/audio-to-midi/abc/transcription.musicxml",
                        "musicxml_optimized": true,
                        "notes": [
                          {
                            "pitch": 60,
                            "start": 0,
                            "end": 0.5,
                            "instrument": "piano",
                            "velocity": 80
                          }
                        ],
                        "duration_seconds": 30.5,
                        "note_count": 1,
                        "tempo_bpm": 120,
                        "timing": {
                          "requested": "quantized",
                          "applied": "quantized",
                          "subdivision": {
                            "version": 1,
                            "requested": "automatic",
                            "applied": "straight_sixteenths",
                            "confidence": 0.92,
                            "regions": [
                              {
                                "start_quarter": 0,
                                "subdivision": "straight_sixteenths",
                                "confidence": 0.92
                              }
                            ]
                          }
                        },
                        "time_grid": {
                          "time_signature": {
                            "numerator": 4,
                            "denominator": 4
                          },
                          "score_quarters_per_detected_beat": 1,
                          "pickup_quarters": 0,
                          "beats_per_bar": 4,
                          "meter_source": "detected",
                          "pickup_beats": 0,
                          "score_origin_seconds": 0,
                          "tempo_bpm": 120,
                          "tempo_source": "caller",
                          "tempo_curve": false
                        }
                      },
                      "request": {
                        "audio": {
                          "type": "url",
                          "audio_url": "https://example.com/song.mp3"
                        },
                        "timing": "quantized",
                        "tempo": {
                          "mode": "fixed",
                          "bpm": 120
                        },
                        "optimize_musicxml": true,
                        "score_pdfs": true,
                        "page_size": "letter"
                      }
                    }
                  },
                  "errored": {
                    "summary": "Job failed — upstream transcription error",
                    "value": {
                      "job_id": "550e8400-e29b-41d4-a716-446655440002",
                      "status": "errored",
                      "created_at": "2024-01-15T10:30:00.000Z",
                      "completed_at": "2024-01-15T10:30:12.000Z",
                      "error": {
                        "code": "generation_failed",
                        "message": "Generation failed due to an upstream error. Retry or contact support if this persists.",
                        "http_status": 502
                      },
                      "request": {
                        "audio": {
                          "type": "url",
                          "audio_url": "https://example.com/song.mp3"
                        },
                        "timing": "quantized",
                        "tempo": {
                          "mode": "fixed",
                          "bpm": 120
                        },
                        "optimize_musicxml": true,
                        "score_pdfs": true,
                        "page_size": "letter"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — missing or malformed parameters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status",
                        "credits_required",
                        "credits_available",
                        "credit_shortfall",
                        "recovery_action",
                        "recovery_url",
                        "provisioning_state",
                        "provisioning_deadline"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Job not found or not owned by this API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — `error.code` is `rate_limited`. Either this API key exceeded its own request ceiling, an input URL's origin is throttling us (a short download link followed hard enough to be throttled does this), or an upstream model provider is throttling us. All three mean retry: the request and any URLs in it are still valid. `Retry-After` is present whenever the wait is known. Async jobs report the same code on the poll response.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Omitted when the wait is unknown.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message",
                        "http_status"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code from the closed taxonomy"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable description of the error"
                        },
                        "http_status": {
                          "type": "integer",
                          "description": "HTTP status code mirroring the response status"
                        },
                        "credits_required": {
                          "type": "integer",
                          "description": "Exact credits required by the request"
                        },
                        "credits_available": {
                          "type": "integer",
                          "description": "Reservation-aware financial capacity, including enabled overage headroom; non-ready provisioning can still block spending"
                        },
                        "credit_shortfall": {
                          "type": "integer",
                          "description": "Additional financial capacity required; 0 means capacity covers the request, but non-ready provisioning can still block it"
                        },
                        "recovery_action": {
                          "type": "string",
                          "enum": [
                            "enable_overage",
                            "increase_overage_limit",
                            "view_plans",
                            "contact_support"
                          ],
                          "nullable": true,
                          "description": "Server-recommended next step; honor a non-null action even when credit_shortfall is 0"
                        },
                        "recovery_url": {
                          "type": "string",
                          "format": "uri",
                          "nullable": true,
                          "description": "Destination for recovery_action"
                        },
                        "provisioning_state": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "pending",
                            "stalled"
                          ],
                          "nullable": true,
                          "description": "Included-credit grant state. Any non-ready state blocks spending; pending waits for provisioning_deadline and stalled follows recovery_action."
                        },
                        "provisioning_deadline": {
                          "type": "integer",
                          "nullable": true,
                          "description": "ms epoch when pending provisioning becomes stalled"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {},
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "x-mirelo-version": "2026-08-28"
}