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

# Run GPT Image 2.5 Flare (gpt-image-2.5-flare)

> OpenAI's GPT Image 2.5 Flare; automatically selects rendering quality for the prompt.

Submits an asynchronous job and returns `202` with a job id. Fetch the result at `GET /v3/jobs/{job_id}` — each item in its `outputs[]` follows the `OutputItem` schema — or track progress via `GET /v3/jobs/{job_id}/status` / the SSE stream at `GET /v3/jobs/{job_id}/stream`.



## OpenAPI

````yaml /openapi-v3.json post /models/gpt-image-2.5-flare
openapi: 3.1.0
info:
  title: Hedra API v3
  description: >-
    Generate images, video, and audio with over 100 models through one API,
    including Veo 3.1, Seedance 2.0, Kling V3, GPT Image 2, Nano Banana Pro, and
    ElevenLabs V3. Create an API key and fund your wallet, then every model runs
    through the same three requests: submit a job, poll it, download the output.
    Input fields have the same names across models, such as `prompt`,
    `aspect_ratio`, `resolution`, `duration_ms`, and `start_image`, so switching
    models means changing the model id and choosing values the new model's
    schema accepts. Browse the [model
    catalog](https://www.hedra.com/develop/models) to pick one.


    **Authenticate** every request with your API key — create one in the
    [developer console](https://www.hedra.com/develop/api-keys): `Authorization:
    Key <key_id>:<secret>`.


    **Fund the API wallet** before your first call. It is prepaid and held in US
    dollars, it is separate from your Hedra Studio balance, and a workspace
    starts with **$0.00** in it; nothing you buy or hold in Studio moves money
    into it. Until it is funded, every generation is refused with `402
    INSUFFICIENT_BALANCE`; uploading inputs with `POST /files` is free and works
    on an empty wallet, so you can stage a request and only meet the gate at
    submit. Add funds in the [developer
    console](https://www.hedra.com/develop/billing), and read the wallet any
    time with `GET /balance`.


    **Quickstart** — submit a job, poll it, then read the output URL:


    ```bash

    # 1. Submit — capture the job id from the 202 ack

    JOB_ID=$(curl -sS -X POST https://api.hedra.com/v3/models/gpt-image-2 \
      -H "Authorization: Key $HEDRA_KEY" \
      -H "Content-Type: application/json" \
      -d '{"input": {"prompt": "a space cat", "quality": "medium", "aspect_ratio": "1:1", "resolution": "1K"}}' | jq -r .job_id)

    # 2. Poll until status is COMPLETED (or FAILED)

    curl https://api.hedra.com/v3/jobs/$JOB_ID/status -H "Authorization: Key
    $HEDRA_KEY"


    # 3. Read the result — outputs[].url holds the generated file

    curl https://api.hedra.com/v3/jobs/$JOB_ID -H "Authorization: Key
    $HEDRA_KEY"

    ```


    **Follow a job without polling** — a video model can run for minutes, so the
    loop above suits fast image jobs and little else. Submit with a `webhook`
    URL and Hedra delivers the finished result there, ed25519-signed and
    retried; set `PUT /webhooks/default` once to cover every job that names no
    URL of its own, and see *Webhooks* for the signature scheme and the retry
    ladder. To watch one job live instead, open `GET /jobs/{job_id}/stream` —
    Server-Sent Events carrying every status transition and lifecycle event
    until the job reaches a terminal state.


    **Find a model** — `GET /models` lists every model with its `id` and human
    `name`; each operation in the *Run a model* section is titled with both, so
    a model you know by name ("GPT Image 2") maps straight to the id you submit
    to (`gpt-image-2`). One model's typed input schema: `GET
    /models/{id}/openapi.json`.


    Estimate cost before you run with `POST /models/{model}/estimate`. For
    volume pricing and custom limits, [talk to
    us](https://www.hedra.com/enterprise).


    The full machine-readable spec is at
    `https://api.hedra.com/v3/openapi.json`.
  version: 3.17.8
servers:
  - url: https://api.hedra.com/v3
security: []
tags:
  - name: Run a model
    description: >-
      One typed submit endpoint per model — titled `Run <name> (<id>)`, so the
      model you know by name is the operation you call. Every one returns the
      same `202` job envelope; track it under Jobs.


      `POST /models/{model}` is the same call with the id supplied at runtime
      and `input` untyped — use it for a model your client was generated before,
      or one you looked up in `GET /models`.
  - name: Models
    description: Discover models and estimate cost.
  - name: Jobs
    description: Poll, stream, and list submitted jobs.
  - name: Webhooks
    description: >-
      Configure, test, verify, and inspect outbound webhooks.


      **Delivery and retries.** Delivery is at-least-once. A `2xx` from your
      endpoint is success; anything else — including a redirect, which is never
      followed — is retried. Hedra makes up to **12 attempts over approximately
      6 hours**, backing off 10s, 30s, 90s, 4m30s, 13m30s, 40m30s, then hourly.
      An endpoint that is unreachable for the whole window is marked `FAILED`
      and is not retried again; replay it with `POST
      /webhooks/deliveries/{job_id}/redeliver`. Two failures are permanent and
      stop the ladder immediately: a URL that resolves to a blocked address
      range, and a redirect pointing at one.


      Because the retry window is bounded, respond quickly and do your own
      processing asynchronously — acknowledge with a `2xx` first.


      **Deduplication.** Key on **`X-Hedra-Webhook-Id`**. It identifies the
      *event* — it is the job's own id, and it is byte-identical across every
      retry and every replay. Do **not** hash the request body: each attempt
      re-signs the output URL, so the body legitimately differs between attempts
      of the same event and a body hash would treat every retry as new.


      Each POST also carries `X-Hedra-Webhook-Attempt` (1-based, informational)
      and **`X-Hedra-Webhook-Redelivery`** (`true` / `false`). Redelivery is the
      exception to deduplication: when it is `true`, an operator asked for this
      event to be sent again — process it even if you have already recorded that
      id, because that request is the whole reason it was sent.


      **Inspecting deliveries.** `GET /webhooks/deliveries` reports each
      delivery's cumulative `attempts`, its latest outcome (`status`,
      `last_response_status`, `last_error`), and its replay history:
      `redelivery_count` counts operator replays, and `redeliveries` archives
      the outcome each replay superseded — a replay re-sends on the same
      delivery record and never erases the previous result. Each entry's
      `attempts` value is the cumulative total when that replay was requested,
      so the per-cycle attempt counts (and any total beyond the 12-attempt
      ladder) can be read off the record directly.


      **Why a delivery failed.** `last_error` — on the delivery and on every
      archived replay entry — is the same error envelope a failed job returns
      from `GET /jobs/{job_id}`, not a free-text diagnostic. Its `code` comes
      from the one error vocabulary this API uses, narrowed for delivery to this
      set:


      | `code` | Means |

      | --- | --- |

      | `DEADLINE_EXCEEDED` | Your endpoint did not respond before the delivery
      timeout. |

      | `UNAVAILABLE` | Your endpoint could not be reached (DNS, TLS, or
      connection failure) or answered `5xx` — or redirected, which is never
      followed. |

      | `RESOURCE_EXHAUSTED` | Your endpoint answered `429`. |

      | `UNAUTHORIZED` | Your endpoint answered `401`. |

      | `PERMISSION_DENIED` | Your endpoint answered `403`. |

      | `NOT_FOUND` | Your endpoint answered `404`. |

      | `INVALID_ARGUMENT` | Your endpoint answered some other `4xx`. |

      | `FAILED_PRECONDITION` | The URL is not a permitted webhook target — it
      resolves to a blocked address range, or redirects to one. Fix the URL;
      replaying will not help. |

      | `INTERNAL` | Hedra could not build the payload. |

      | `UNKNOWN` | No classification is available, including for deliveries
      that failed before this field became structured. |


      `retryable` describes the condition, not what Hedra did: every non-2xx
      response is retried on the ladder above, so it answers whether replaying
      the delivery is likely to help. The `message` is a fixed summary — your
      endpoint's URL, address, headers, credentials, and response body are never
      echoed back, so treat your own logs, not this field, as the record of what
      your endpoint returned.


      **Verifying a delivery.** Every request carries
      `X-Hedra-Webhook-Signature`, a hex-encoded ed25519 signature over a
      canonical string of **five newline-separated fields, in this order**:


      ```

      {X-Hedra-Webhook-Timestamp}

      {X-Hedra-Webhook-Id}

      {X-Hedra-Webhook-Event}

      {X-Hedra-Webhook-Redelivery}

      {sha256 hex digest of the raw request body}

      ```


      Rebuild that string from the request you received, verify it against the
      key from `GET /webhooks/public-key`, and reject the delivery if the
      timestamp is more than 5 minutes old. Hash the body **exactly as
      received**, before any JSON parsing or re-serialization.


      The signature deliberately covers the deduplication id and the redelivery
      flag, not just the body — both of them decide whether you process a
      duplicate, so an unsigned copy of either would let anyone who captured a
      delivery replay it past your idempotency check. Verify *before* you act on
      any header. `X-Hedra-Webhook-Attempt` is outside the signature and is
      informational only; do not branch on it.
  - name: Log drains
    description: >-
      Stream signed job lifecycle logs to HTTPS destinations.


      **Why the last batch failed.** `last_error` is the same error envelope a
      failed job returns from `GET /jobs/{job_id}`, not a free-text diagnostic.
      It is null until a batch fails and is cleared again on the next success.
      Its `code` comes from the one error vocabulary this API uses, narrowed for
      drain delivery to this set:


      | `code` | Means |

      | --- | --- |

      | `DEADLINE_EXCEEDED` | Your destination did not respond before the
      delivery timeout. |

      | `UNAVAILABLE` | Your destination could not be reached (DNS, TLS, or
      connection failure) or answered `5xx` — or redirected, which is never
      followed. |

      | `RESOURCE_EXHAUSTED` | Your destination answered `429`. |

      | `UNAUTHORIZED` | Your destination answered `401`. |

      | `PERMISSION_DENIED` | Your destination answered `403`. |

      | `NOT_FOUND` | Your destination answered `404`. |

      | `INVALID_ARGUMENT` | Your destination answered some other `4xx`. |

      | `FAILED_PRECONDITION` | The URL is not a permitted log drain target — it
      resolves to a blocked address range. Fix the URL; retrying will not help.
      |

      | `INTERNAL` | Hedra could not prepare the batch. |

      | `UNKNOWN` | No classification is available, including for drains that
      last failed before this field became structured. |


      `retryable` describes the condition, not what Hedra did: every failed
      batch is requeued until the drain auto-disables, so it answers whether
      fixing the destination and re-enabling is likely to help. The `message` is
      a fixed summary — your destination's URL, headers, credentials, and
      response body are never echoed back, so treat your own logs, not this
      field, as the record of what your destination returned.


      `disabled_reason` answers a different question and stays its own small
      vocabulary: `consecutive_failures` when five batches in a row failed,
      `disabled_by_user` when you turned the drain off.
  - name: Access
    description: API keys, browser tokens, and file uploads.
  - name: Billing
    description: Balance and usage, in US dollars.
paths:
  /models/gpt-image-2.5-flare:
    post:
      tags:
        - Run a model
      summary: Run GPT Image 2.5 Flare (gpt-image-2.5-flare)
      description: >-
        OpenAI's GPT Image 2.5 Flare; automatically selects rendering quality
        for the prompt.


        Submits an asynchronous job and returns `202` with a job id. Fetch the
        result at `GET /v3/jobs/{job_id}` — each item in its `outputs[]` follows
        the `OutputItem` schema — or track progress via `GET
        /v3/jobs/{job_id}/status` / the SSE stream at `GET
        /v3/jobs/{job_id}/stream`.
      operationId: submit_gpt_image_2.5_flare
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitBody_gpt_image_2.5_flare'
      responses:
        '202':
          description: >-
            Accepted. The job runs asynchronously; poll `status_url` /
            `result_url` from the ack.
          headers:
            Location:
              description: >-
                Path of the job resource this submit created — the same value as
                the ack's `result_url`.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubmitResponse'
        '400':
          description: Invalid or failed-validation request.
          content:
            application/json:
              examples:
                field_validation:
                  summary: A submit `input` field failed schema validation
                  value:
                    error:
                      code: INVALID_ARGUMENT
                      message: 2 input fields are invalid; see details.
                      retryable: false
                      param: input.aspect_ratio
                      details:
                        - field: input.aspect_ratio
                          message: >-
                            input.aspect_ratio: Input should be '16:9', '9:16'
                            or '1:1'
                          reason: enum
                        - field: input.duration_ms
                          message: >-
                            input.duration_ms: Input should be greater than or
                            equal to 1000
                          reason: minimum
                expired_file_handle:
                  summary: >-
                    A `POST /v3/files` handle was submitted past its
                    `expires_at`
                  value:
                    error:
                      code: INVALID_ARGUMENT
                      message: >-
                        input.images[0]: the POST /v3/files url expired at
                        2026-07-28T23:31:12+00:00. Upload the file again and
                        submit with the fresh url.
                      retryable: false
                      param: input.images[0]
                      details:
                        - field: input.images[0]
                          message: >-
                            input.images[0]: the POST /v3/files url expired at
                            2026-07-28T23:31:12+00:00. Upload the file again and
                            submit with the fresh url.
                          reason: expired
                unsupported_model:
                  summary: The model exists but isn't wired to v3 yet
                  value:
                    error:
                      code: FAILED_PRECONDITION
                      message: >-
                        Model 'some-model' (VIDEO_STITCHING) is not supported on
                        v3 yet.
                      retryable: false
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: 'Authentication failed: missing, malformed, or invalid credentials.'
          content:
            application/json:
              examples:
                missing_credentials:
                  summary: No credential on the request
                  value:
                    error:
                      code: UNAUTHORIZED
                      message: >-
                        Missing credentials. Send 'Authorization: Key
                        <key_id>:<secret>' or 'Authorization: Bearer <token>'.
                      retryable: false
                invalid_key:
                  summary: Credential present but not valid
                  value:
                    error:
                      code: UNAUTHORIZED
                      message: Invalid API key.
                      retryable: false
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '402':
          description: >-
            The API wallet cannot cover this request. `error.billing` carries
            the balance, the price, and where to add funds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unfunded_submit:
                  summary: The API wallet cannot pay for this job
                  value:
                    error:
                      code: INSUFFICIENT_BALANCE
                      message: >-
                        Your API wallet balance is $0.00 and this request costs
                        $0.05. The API wallet is funded separately from Studio
                        and pays only for API requests — add funds at
                        https://www.hedra.com/develop/billing, then retry.
                      retryable: false
                      billing:
                        balance: 0
                        required: 0.05
                        currency: USD
                        funding_url: https://www.hedra.com/develop/billing?add_funds=true
                unquotable_submit:
                  summary: >-
                    The model cannot be priced before its inputs are measured,
                    so `required` is null
                  value:
                    error:
                      code: INSUFFICIENT_BALANCE
                      message: >-
                        Your API wallet balance is $0.00. The API wallet is
                        funded separately from Studio and pays only for API
                        requests — add funds at
                        https://www.hedra.com/develop/billing, then retry.
                      retryable: false
                      billing:
                        balance: 0
                        currency: USD
                        funding_url: https://www.hedra.com/develop/billing?add_funds=true
        '403':
          description: Insufficient scope or plan.
          content:
            application/json:
              examples:
                no_workspace_access:
                  summary: Key no longer reaches its workspace
                  value:
                    error:
                      code: PERMISSION_DENIED
                      message: The key's owner is no longer a member of its workspace.
                      retryable: false
                missing_scope:
                  summary: Key lacks a scope the operation requires
                  value:
                    error:
                      code: PERMISSION_DENIED
                      message: 'Key is missing required scope(s): jobs:write.'
                      retryable: false
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found.
          content:
            application/json:
              examples:
                job_not_found:
                  summary: Unknown job id, or not visible to the caller
                  value:
                    error:
                      code: NOT_FOUND
                      message: Job not found.
                      retryable: false
                model_not_found:
                  summary: No such model in the catalog
                  value:
                    error:
                      code: NOT_FOUND
                      message: Model 'some-model' not found.
                      retryable: false
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Moderation refused the request's content.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                moderation_failed:
                  summary: A reference image was refused by moderation
                  value:
                    error:
                      code: MODERATION_FAILED
                      message: Image failed moderation.
                      retryable: false
        '429':
          description: Rate limit exceeded.
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            X-RateLimit-Limit:
              description: Request budget for the current window.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests left in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Unix epoch second the window resets at.
              schema:
                type: integer
          content:
            application/json:
              examples:
                rate_limited:
                  summary: Per-key request rate limit exceeded
                  value:
                    error:
                      code: RESOURCE_EXHAUSTED
                      message: >-
                        Request rate limit exceeded: this API key is limited to
                        60 requests per 60s. Retry after 30s. To follow job
                        progress without polling, stream job status
                        (https://www.hedra.com/docs/api-reference/v3/jobs/stream-job)
                        or subscribe to webhooks
                        (https://www.hedra.com/docs/api-reference/v3/webhooks).
                      retryable: true
                      retry_after: 30
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error.
          content:
            application/json:
              examples:
                internal:
                  summary: Unexpected server error; internals are never leaked
                  value:
                    error:
                      code: INTERNAL
                      message: An unexpected error occurred
                      retryable: false
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - KeyAuth: []
        - BearerToken: []
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST "https://api.hedra.com/v3/models/gpt-image-2.5-flare" \
              -H "Authorization: Bearer $HEDRA_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
              "input": {
                "aspect_ratio": "16:9",
                "prompt": "prompt",
                "resolution": "1K"
              }
            }'
        - lang: python
          label: hedra-python
          source: |
            from hedra import Hedra, InputGptImage25Flare

            client = Hedra(api_key="YOUR_API_KEY")

            client.jobs.submit_gpt_image25flare(
                input=InputGptImage25Flare(
                    prompt="prompt",
                    aspect_ratio="16:9",
                    resolution="1K",
                ),
            )
        - lang: typescript
          label: hedra-node
          source: |
            import { HedraClient } from "hedra-node";

            const client = new HedraClient({ apiKey: "YOUR_API_KEY" });

            await client.jobs.submitGptImage25Flare({
                input: {
                    prompt: "prompt",
                    aspect_ratio: "16:9",
                    resolution: "1K"
                }
            });
components:
  schemas:
    SubmitBody_gpt_image_2.5_flare:
      type: object
      properties:
        input:
          $ref: '#/components/schemas/Input_gpt_image_2.5_flare'
        webhook:
          type:
            - string
            - 'null'
          format: uri
          description: URL to receive a signed completion webhook.
          default: null
        idempotency_key:
          type:
            - string
            - 'null'
          description: >-
            Replays the original ack for a retried submit instead of enqueueing
            a duplicate job.
          default: null
      required:
        - input
    SubmitResponse:
      properties:
        job_id:
          description: This job's id — server-issued, and opaque.
          title: Job Id
          type: string
        model:
          description: The resolved model id this job runs on.
          title: Model
          type: string
        status:
          $ref: '#/components/schemas/JobStatus'
        status_url:
          description: >-
            Path of this job's status monitor: poll GET /v3/jobs/{job_id}/status
            for status, progress, and an estimate.
          title: Status Url
          type: string
        result_url:
          description: >-
            Path of the job resource itself: GET /v3/jobs/{job_id} returns the
            result envelope, including the outputs once it completes. Also the
            value of this response's `Location` header.
          title: Result Url
          type: string
        estimated_completion_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          default: null
          description: >-
            ISO-8601 instant this job is estimated to finish. Null when no
            estimate exists for the model yet; poll GET /v3/jobs/{job_id}/status
            for a refreshed one.
          title: Estimated Completion At
      required:
        - job_id
        - model
        - status
        - status_url
        - result_url
      title: SubmitResponse
      type: object
    ErrorResponse:
      properties:
        error:
          $ref: '#/components/schemas/ErrorEnvelope'
        trace_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Trace Id
          description: >-
            Debug id to quote to support (32-char hex trace id), also emitted as
            the `X-Trace-Id` response header.
      type: object
      required:
        - error
      title: ErrorResponse
      description: 'Top-level error body: ``{"error": {...}}`` plus a debug id.'
    Input_gpt_image_2.5_flare:
      description: Model-specific inputs for `gpt-image-2.5-flare`.
      type: object
      properties:
        prompt:
          description: Generation prompt. At most 32000 characters.
          maxLength: 32000
          pattern: '[^\s\x1C-\x1F]'
          type: string
        num_outputs:
          default: 1
          description: Number of outputs generated per job. Only 1 is supported.
          type: integer
          enum:
            - 1
        enhance_prompt:
          default: false
          description: >-
            Rewrite the prompt before generation. An LLM expands it into a
            fuller description and the model receives that text instead of the
            submitted one; the result's `prompt` reports what ran.
          type: boolean
        aspect_ratio:
          description: Output aspect ratio.
          enum:
            - '16:9'
            - '9:16'
            - '4:3'
            - '3:4'
            - '1:1'
          type: string
        resolution:
          description: Output resolution.
          enum:
            - 1K
            - 2K
            - 4K
          type: string
        output_format:
          enum:
            - jpeg
            - png
            - webp
          type: string
          description: Output image format.
        images:
          description: Images to edit or blend. 1 to 16 images, each at most 30 MB.
          items:
            discriminator:
              propertyName: source
            oneOf:
              - additionalProperties: false
                description: >-
                  A file uploaded via POST /v3/files, referenced by the returned
                  url.
                properties:
                  source:
                    type: string
                    enum:
                      - url
                  url:
                    description: A URL returned by POST /v3/files.
                    format: uri
                    type: string
                required:
                  - source
                  - url
                type: object
              - additionalProperties: false
                description: An existing asset you own, referenced by its id.
                properties:
                  source:
                    type: string
                    enum:
                      - asset
                  asset_id:
                    description: >-
                      The asset's id (`asset_<uuid>`), as issued by the server —
                      a completed generation publishes one per output as
                      `outputs[].asset_id`. Do not construct one or derive it
                      from any other id.
                    type: string
                required:
                  - source
                  - asset_id
                type: object
          maxItems: 16
          media_type: image
          minItems: 1
          role: reference
          type: array
          max_file_size_bytes: 30000000
        quality:
          type: string
          enum:
            - auto
            - low
            - medium
            - high
          description: >-
            Quality level to generate at. `auto` — automatically selects
            rendering quality for the prompt. `low` — low rendering quality for
            the Flare model. `medium` — medium rendering quality for the Flare
            model. `high` — high rendering quality for the Flare model.
          default: auto
      required:
        - aspect_ratio
        - prompt
        - resolution
      additionalProperties: false
      anyOf:
        - required:
            - aspect_ratio
            - images
            - prompt
            - resolution
        - required:
            - aspect_ratio
            - prompt
            - resolution
          properties:
            images: false
    JobStatus:
      type: string
      enum:
        - IN_QUEUE
        - IN_PROGRESS
        - COMPLETED
        - FAILED
      title: JobStatus
      description: The four states a job can be in.
    ErrorEnvelope:
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          title: Message
          description: >-
            Human-readable summary of the error. Fixed per condition — match on
            `code`, not on this text.
        retryable:
          type: boolean
          title: Retryable
          description: >-
            Whether retrying the same request can succeed. Describes the
            condition, not a promise — pair with `retry_after` when present.
          default: false
        retry_after:
          anyOf:
            - type: integer
            - type: 'null'
          title: Retry After
          description: >-
            Seconds to wait before retrying; set when the error is retryable.
            Mirrors the `Retry-After` response header.
        param:
          anyOf:
            - type: string
            - type: 'null'
          title: Param
          description: >-
            The primary offending input field, when the error is about one
            specific field.
        details:
          anyOf:
            - items:
                $ref: '#/components/schemas/FieldError'
              type: array
            - type: 'null'
          title: Details
          description: >-
            Every field-level problem, when the error is a validation failure —
            all of them at once, not just the first.
        replaced_by:
          anyOf:
            - type: string
            - type: 'null'
          title: Replaced By
          description: >-
            The id of a successor model, set when the requested model has been
            retired (code `GONE`) and replaced; null otherwise. Lets a client
            programmatically migrate off a retired model.
        billing:
          anyOf:
            - $ref: '#/components/schemas/BillingError'
            - type: 'null'
          description: >-
            Balance, price, and where to add funds — set when the request was
            refused for funds (code `INSUFFICIENT_BALANCE`); null otherwise.
      type: object
      required:
        - code
        - message
      title: ErrorEnvelope
    ErrorCode:
      type: string
      enum:
        - UNKNOWN
        - INVALID_ARGUMENT
        - NOT_FOUND
        - GONE
        - ALREADY_EXISTS
        - ALREADY_IN_PROGRESS
        - UNAUTHORIZED
        - PERMISSION_DENIED
        - INSUFFICIENT_BALANCE
        - MODERATION_FAILED
        - FAILED_PRECONDITION
        - CANCELLED
        - DEADLINE_EXCEEDED
        - RESOURCE_EXHAUSTED
        - UNAVAILABLE
        - INTERNAL
      title: ErrorCode
      description: >-
        Universal error code space. Modeled after gRPC status codes.


        Every exception in the system carries an ErrorCode. SDK clients map

        3rd-party errors to these codes at the lowest level. Retry logic,

        HTTP status mapping, and OTEL metrics all key off this enum.


        NOTE: The semantic meaning of these error codes is roughly mapped from
        the
            semantic of the gRPC status codes: see
            https://grpc.io/docs/guides/status-codes/ for details.
    FieldError:
      properties:
        field:
          type: string
          title: Field
          description: Dotted path to the offending input (e.g. `input.resolution`).
        message:
          type: string
          title: Message
          description: What is wrong with this field's value.
        reason:
          anyOf:
            - type: string
            - type: 'null'
          title: Reason
          description: >-
            Machine-readable hint for which constraint failed ("required",
            "enum", "type", …).
        allowed:
          anyOf:
            - items:
                type: string
              type: array
            - type: 'null'
          title: Allowed
          description: >-
            Accepted values for an enum, or accepted field names on the first
            unknown-field violation. Omitted on subsequent unknown fields.
      type: object
      required:
        - field
        - message
      title: FieldError
      description: >-
        One field-level validation problem in a request body.


        `field` is the dotted path to the offending input (e.g.
        `input.resolution`);

        `allowed` lists the accepted values when the field is an enum, so a
        caller

        can fix it without re-fetching the model schema.
    BillingError:
      properties:
        balance:
          type: number
          title: Balance
          description: >-
            Spendable balance on the account this request bills, at the moment
            it was refused. The same value `GET /balance` returns.
        required:
          anyOf:
            - type: number
            - type: 'null'
          title: Required
          description: >-
            What this request needed. Null when the refusal is a funding
            precondition rather than a priced one — an upload is not priced, and
            some models cannot be quoted until their inputs are measured (`POST
            /models/{model}/estimate` says so explicitly).
        currency:
          type: string
          title: Currency
          description: ISO-4217 code the amounts above are denominated in.
          default: USD
        funding_url:
          type: string
          title: Funding Url
          description: >-
            Where a human can add funds to the account this request bills. The
            API itself cannot add them.
        reason:
          anyOf:
            - type: string
            - type: 'null'
          title: Reason
          description: >-
            Why the automatic top-up did not fund the balance, when known.
            `card_declined`: the card that pays for automatic top-ups was
            declined, and the payment method must be updated on the billing page
            before a top-up can succeed. Clients MUST tolerate unrecognized
            values: new reasons may be added at any time.
      type: object
      required:
        - balance
        - funding_url
      title: BillingError
      description: >-
        Why a request was refused for funds, and what would clear it.


        The envelope's structured detail for `INSUFFICIENT_BALANCE`, the way

        `details` is its structured detail for a validation failure. Every
        amount is

        in `currency`, and `balance` is the same number `GET /balance` returns —
        so

        a client can decide whether to top up, wait, or fail over without a
        second

        round trip to work out which balance was short.
  securitySchemes:
    KeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: 'Primary scheme: `Authorization: Key <key_id>:<secret>`.'
    BearerToken:
      type: http
      scheme: bearer
      description: >-
        `Authorization: Bearer <credential>` — accepts an API key
        (`<key_id>:<secret>`) or an ephemeral browser token minted via `POST
        /tokens` (inherits the minting key's scopes and workspace).

````