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

# List the models Comfy Router can run.

> Comfy Router's model catalog - one page of the canonical model IDs that `POST /v1/models/{provider}/{model}` accepts. An SDK calls this on cold start to discover what is runnable, and the `model_not_found` suggestions come from the same catalog, so an ID listed here that then 404s on invocation would be worse than either failure alone. That agreement is structural rather than a promise: an entry's `provider` and `model` are the two path segments of the invocation route and reference the SAME schema components that route's path parameters do, and `id` is those two segments joined by `/`.
Pagination is CURSOR-based, deliberately not offset-based. The catalog is a moving list - models are added, and embargoed, between calls - and an offset walk silently skips or repeats entries when the list changes underneath it. Pass a page's `next_cursor` back as `cursor` to fetch the next page, and stop when `has_more` is false rather than when a page comes back short. A cursor is opaque: it is not an offset, not a model ID, and not stable across catalog rebuilds, so a cursor that is malformed or no longer valid is answered with a `400` from the Router error contract (`error_type: invalid_input`), never a `500`.
A model that is deployed but NOT yet released is EXCLUDED from every page. This is a requirement of this route specifically, not something it inherits: `PartnerModelEmbargoMiddleware` gates `/proxy/*` only, and only methods that can carry a body, so a bodyless `GET` outside `/proxy/` is outside the embargo gate on both axes. Confirming that a specific unreleased model exists is precisely the disclosure `modelembargo` was built to prevent, and a catalog is the most direct way to make that confirmation - so the handler must filter the embargo set out of the page itself. An excluded model is simply absent: the catalog does not mark it, does not reserve a slot for it, and `has_more`/`limit` describe the page AFTER exclusion, so the omission is not inferable from a short page either.
Per-model detail and the per-model input/output schemas are separate routes; this one carries only the identity of each model.



## OpenAPI

````yaml https://api.comfy.org/openapi get /v1/models
openapi: 3.0.2
info:
  title: Comfy API
  version: '1.0'
servers:
  - url: https://api.comfy.org
security: []
tags:
  - description: Comfy Router's canonical, model-ID-addressed routes.
    name: Comfy Router
paths:
  /v1/models:
    get:
      tags:
        - Comfy Router
      summary: List the models Comfy Router can run.
      description: >-
        Comfy Router's model catalog - one page of the canonical model IDs that
        `POST /v1/models/{provider}/{model}` accepts. An SDK calls this on cold
        start to discover what is runnable, and the `model_not_found`
        suggestions come from the same catalog, so an ID listed here that then
        404s on invocation would be worse than either failure alone. That
        agreement is structural rather than a promise: an entry's `provider` and
        `model` are the two path segments of the invocation route and reference
        the SAME schema components that route's path parameters do, and `id` is
        those two segments joined by `/`.

        Pagination is CURSOR-based, deliberately not offset-based. The catalog
        is a moving list - models are added, and embargoed, between calls - and
        an offset walk silently skips or repeats entries when the list changes
        underneath it. Pass a page's `next_cursor` back as `cursor` to fetch the
        next page, and stop when `has_more` is false rather than when a page
        comes back short. A cursor is opaque: it is not an offset, not a model
        ID, and not stable across catalog rebuilds, so a cursor that is
        malformed or no longer valid is answered with a `400` from the Router
        error contract (`error_type: invalid_input`), never a `500`.

        A model that is deployed but NOT yet released is EXCLUDED from every
        page. This is a requirement of this route specifically, not something it
        inherits: `PartnerModelEmbargoMiddleware` gates `/proxy/*` only, and
        only methods that can carry a body, so a bodyless `GET` outside
        `/proxy/` is outside the embargo gate on both axes. Confirming that a
        specific unreleased model exists is precisely the disclosure
        `modelembargo` was built to prevent, and a catalog is the most direct
        way to make that confirmation - so the handler must filter the embargo
        set out of the page itself. An excluded model is simply absent: the
        catalog does not mark it, does not reserve a slot for it, and
        `has_more`/`limit` describe the page AFTER exclusion, so the omission is
        not inferable from a short page either.

        Per-model detail and the per-model input/output schemas are separate
        routes; this one carries only the identity of each model.
      operationId: ListRouterModels
      parameters:
        - $ref: '#/components/parameters/RouterCatalogCursor'
        - $ref: '#/components/parameters/RouterCatalogLimit'
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouterModelListResponse'
          description: OK - one page of the model catalog.
          headers:
            X-Comfy-Request-Id:
              $ref: '#/components/headers/RouterRequestIdHeader'
        '400':
          $ref: '#/components/responses/RouterRequestError'
        '401':
          $ref: '#/components/responses/RouterRequestError'
        '403':
          $ref: '#/components/responses/RouterRequestError'
        '503':
          $ref: '#/components/responses/RouterRequestError'
      security:
        - BearerAuth: []
components:
  parameters:
    RouterCatalogCursor:
      description: >-
        Opaque pagination cursor. Pass a previous page's `next_cursor` to fetch
        the next page; omit it for the first page. See `RouterPageCursor` for
        why the value is opaque and why this route paginates by cursor rather
        than by offset.

        A cursor that is malformed, over-long, or no longer valid is answered
        with a `400` carrying the Router error contract's `RouterErrorResponse`
        (`error_type: invalid_input`) - never a `500`, and never a silent
        fallback to the first page, which would make a walk loop forever.
      in: query
      name: cursor
      schema:
        $ref: '#/components/schemas/RouterPageCursor'
    RouterCatalogLimit:
      description: >-
        Number of models to return in one page. Values above the declared
        maximum are outside the contract, but this route does not reject them:
        it serves the maximum instead, and the page size actually served is
        echoed back as `limit` on the response, so a clamp is always detectable
        by the caller. Treat the maximum as the real page stride - a client that
        asks for more and assumes it received more will miss rows. 0 and
        negative values are also accepted and select the default, which is why
        no `minimum` is declared: sub-1 is meaningful here, not invalid.

        The cap of 100 is the one the node-listing endpoints already use
        (BE-8098): an uncapped page size on a list route is a trivially
        exploitable amplification, and this route is hit by SDKs on cold start.
      in: query
      name: limit
      schema:
        default: 20
        maximum: 100
        type: integer
  schemas:
    RouterModelListResponse:
      description: >-
        One page of the Router model catalog.

        `has_more` is the ONLY correct stop condition for a walk: a short page
        is not one, because a page can be trimmed by an entry that disappeared
        between the cursor being minted and the page being served. `next_cursor`
        is present exactly when `has_more` is true, and omitted otherwise.
        `limit` echoes the page size actually served, which is what makes a
        clamped request detectable.
      properties:
        data:
          description: The models on this page, at most `limit` of them.
          items:
            $ref: '#/components/schemas/RouterModelListEntry'
          type: array
        has_more:
          description: >-
            Whether another page exists beyond this one. Keep walking while this
            is true; do not infer the end of the catalog from a short or empty
            `data`.
          type: boolean
        limit:
          description: >-
            The page size actually served. A requested `limit` above the maximum
            is CLAMPED down to the maximum rather than rejected, so this can be
            smaller than the value asked for - paginate with this number, not
            with the one you sent, or you will assume rows you never received.

            Unlike the REQUEST parameter, this one declares a `minimum`: sub-1
            is meaningful on the way in (it selects the default) but a page size
            actually served is always positive, and `0` is exactly what an unset
            Go field serializes to - so without the bound a handler that forgets
            to populate this still emits a conforming response, and `limit: 0`
            beside `has_more: true` describes a walk that cannot advance.
          example: 20
          maximum: 100
          minimum: 1
          type: integer
        next_cursor:
          $ref: '#/components/schemas/RouterPageCursor'
      required:
        - data
        - has_more
        - limit
      type: object
    RouterPageCursor:
      description: >-
        An OPAQUE cursor into a Router list. It is produced by the server and
        only ever round-tripped: it is not an offset, not a model ID, not
        ordered, and not stable across catalog rebuilds, so parsing one,
        incrementing one, or persisting one beyond the walk it came from are all
        outside the contract. Cursor rather than offset because the catalog is a
        moving list - an offset walk silently skips or repeats entries when
        entries are added or removed mid-walk, and a caller cannot tell that it
        happened.

        `maxLength` bounds it because the value arrives in a query string and is
        fed to a decoder; a cursor that is malformed, truncated, over-long or no
        longer valid is a `400` from the Router error contract, never a `500`.
        `minLength: 1` is load-bearing rather than tidy: Echo's `QueryParam`
        returns `""` for both `?cursor=` and an omitted `cursor`, so without it
        the empty string is a schema-valid cursor indistinguishable from no
        cursor at all, and a handler would silently restart the walk at page one
        - the infinite loop `RouterCatalogCursor` explicitly forbids. An empty
        `cursor` is therefore a `400`, not page one. The `pattern` fixes the
        alphabet at URL- and base64-safe characters so a control character, a
        CR/LF, or a space can never ride a query string into the decoder or into
        a `400`'s free-text `detail`. It constrains the SERVER, which is the
        only party that mints these; as with the model-ID segments it is a
        CONTRACT statement and not enforcement, since comfy-api installs no
        OpenAPI request validator - a handler must still validate the value it
        was handed before decoding it.
      example: q7Fm2xTn9pLd4RsV
      maxLength: 512
      minLength: 1
      pattern: ^[A-Za-z0-9._~+/=-]+$
      type: string
    RouterModelListEntry:
      description: >-
        One entry in the Router model catalog: the identity of a runnable model,
        and nothing else. The per-model detail route composes this same entry
        rather than restating it, which is why the name is `...ListEntry` and
        not `...Summary` - there must be exactly one definition of what a
        catalog entry is. Per-model detail and the per-model input/output
        schemas are their own routes, so this shape stays the minimum a caller
        needs in order to invoke the model - deliberately, because this is the
        payload an SDK fetches on cold start. `id` is `provider` and `model`
        joined by `/`; the two fields are carried separately as well so a caller
        composes the invocation path without splitting a string.

        `billing` is the one non-identity member, and it is here rather than on
        the detail route deliberately: it exists so a caller can branch BEFORE
        invoking, and the listing is the payload an SDK already has in hand at
        that moment. Pushing it to the detail route would mean one extra round
        trip per model to answer a question asked about every model.
      properties:
        billing:
          $ref: '#/components/schemas/RouterModelBilling'
        id:
          $ref: '#/components/schemas/RouterModelId'
        model:
          $ref: '#/components/schemas/RouterModelSegment'
        provider:
          $ref: '#/components/schemas/RouterProviderSegment'
      required:
        - id
        - provider
        - model
        - billing
      type: object
    RouterErrorResponse:
      description: >-
        Router's request-level error body: what is returned when the request
        never reached the model, or failed for a reason the model itself did not
        report - auth, quota, an unknown model ID, or provider transport. A
        model-level validation failure has its own shape,
        `RouterValidationErrorResponse`, because flattening a FastAPI `detail[]`
        array into this `detail` string would destroy the per-field granularity
        an SDK branches on.
      properties:
        detail:
          description: >-
            Human-readable description of the failure, safe to surface to an end
            user. Not machine-parsed - branch on `error_type` instead.
          type: string
        error_type:
          $ref: '#/components/schemas/RouterErrorType'
      required:
        - detail
        - error_type
      type: object
    RouterModelBilling:
      description: >-
        Per-model billing FACTS a caller needs before invoking - not prices.
        Usage and cost figures never appear here.

        It is an object with one member rather than a flat sibling field because
        more pre-invocation billing facts are coming and a flat
        `billing_charges_*` family would have to be un-flattened later; a nested
        object absorbs them without a breaking rename. The member is `required`
        for the reason its own description gives - "we did not say" and "we do
        not charge" must not be the same wire state.
      properties:
        charges_on_policy_rejection:
          $ref: '#/components/schemas/RouterChargesOnPolicyRejection'
      required:
        - charges_on_policy_rejection
      type: object
    RouterModelId:
      description: >-
        A canonical Comfy Router model ID, `{provider}/{model}` - exactly the
        value that addresses the model on `POST /v1/models/{provider}/{model}`,
        so a caller can interpolate it into that path without re-deriving it
        from anything. Its `pattern` is `RouterProviderSegment` and
        `RouterModelSegment` joined by a single `/`, and `maxLength` is their
        sum plus that separator.

        TestRouterCatalogIdsMatchInvocationRoute probes all three patterns
        behaviourally against one corpus, so loosening or tightening any of them
        alone fails CI rather than silently letting the catalog advertise an ID
        the invocation route would reject. The optional `variant` third segment
        is deliberately absent: how a variant is addressed is not settled by the
        invocation contract, so the catalog must not list an ID that route is
        not yet defined to accept. ONE bound does not survive the composition:
        `maxLength` here is the TOTAL, so a 193-character ID made of a
        100-character provider and a 92-character model satisfies this schema
        while its provider segment exceeds `RouterProviderSegment`'s own 64. A
        single `pattern` cannot express a per-segment length bound - the
        structural alphabet and a character count are not jointly expressible
        without lookahead, which this repo's Go-side pattern probes cannot
        compile. The per-segment bounds are therefore carried by the sibling
        `provider` and `model` fields of `RouterModelListEntry`, which reference
        the bounded segment schemas directly, so no CONFORMING entry can carry
        such an `id`. TestRouterCatalogIdsMatchInvocationRoute pins that this
        residual is length-only: over-length segments are in its corpus, and any
        divergence that is not purely a per-segment length overrun fails.
      example: fal-ai/flux-pro
      maxLength: 193
      pattern: ^[a-z0-9]+([._-][a-z0-9]+)*/[a-z0-9]+([._-][a-z0-9]+)*$
      type: string
    RouterModelSegment:
      description: >-
        Lowercase `model` segment of the canonical
        `{provider}/{model}[/{variant}]` model ID - the model to run within that
        provider. Shared by the invocation route's `model` path parameter and a
        catalog entry's `model` field, for the same no-drift reason as
        `RouterProviderSegment`.

        As with the provider segment, the `pattern` documents the contract and
        does not enforce it. Dots are permitted inside the segment because
        partner model IDs use them for versions (`flux-1.1-pro`), but a repeated
        separator is not, so `..` cannot appear.
      example: flux-pro
      maxLength: 128
      pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$
      type: string
    RouterProviderSegment:
      description: >-
        Lowercase `provider` segment of the canonical
        `{provider}/{model}[/{variant}]` model ID - the partner whose model is
        being addressed. The invocation route's `provider` path parameter and a
        catalog entry's `provider` field both reference this one schema, which
        is what keeps the listed IDs and the accepted IDs from drifting apart.

        The `pattern` is a CONTRACT statement, not enforcement: comfy-api
        installs no OpenAPI request validator and oapi-codegen binds path
        parameters as plain strings, so a handler must re-validate the segment
        itself before using it to select a provider or compose an upstream URL.
        The alphabet deliberately admits no `/`, no percent-encoding, and no
        repeated separator, so no accepted value can contain a `.` or `..` path
        segment.
      example: fal-ai
      maxLength: 64
      pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$
      type: string
    RouterErrorType:
      description: >-
        Coarse, machine-readable bucket for a Router failure, mirrored on the
        `X-Comfy-Error-Type` response header so a caller can branch without
        parsing the body. The set is closed at fifteen values: the six
        request-level buckets `invalid_input`, `content_policy_violation`,
        `provider_error`, `provider_timeout`, `insufficient_credits` and
        `model_not_found`, plus the transport-level `unauthorized`, `forbidden`,
        `concurrency_limit_exceeded`, `client_disconnected`, `internal_error`,
        `deadline_exceeded`, `not_enabled`, `service_unavailable` and
        `rate_limited`.

        RETRY SEMANTICS. `not_enabled` is TERMINAL - the same request will be
        refused the same way, so a client must not retry it - while
        `service_unavailable` is TRANSIENT and SHOULD be retried with backoff.
        That opposition is the whole reason they are two buckets rather than
        one: a retry policy keys off the status/`error_type` pair, and a single
        shared bucket would make retry wrong in one direction or the other.

        `not_enabled` and `forbidden` both return `403` and, like the `504` pair
        below, the difference between them is the one the status cannot carry.
        `forbidden` means the credential is valid but is not entitled to this
        model or operation - a decision about the caller. `not_enabled` means
        Comfy Router is not switched on for the caller yet - a state of the
        product rollout, not a judgement about them, and one that changes
        without the caller doing anything. `404` would have claimed the model
        does not exist when it does, and a `5xx` would have blamed the server
        for a deliberate state, so neither was available as a way to separate
        the two by status instead.

        HOW OFTEN `not_enabled` IS SEEN CHANGES OVER TIME, AND IT NEVER STOPS
        BEING VALID. While Comfy Router is rolling out, most callers are not on
        the ramp yet and this is the ordinary answer for them. Once Router is
        fully rolled out it becomes rare. It does not become impossible: the
        deploy-time switch that turns Router off for an environment is a
        permanent operational lever, so `not_enabled` is still the correct
        answer the day it is pulled. It is therefore a value Comfy expects to
        STOP EMITTING often rather than one that is ever withdrawn - a published
        bucket is never removed, because removing it would delete the generated
        exception class an SDK built from it and break any client branching on
        it. `service_unavailable` has no such arc at all: a dependency can
        always fault.

        `deadline_exceeded` and `provider_timeout` both return `504` and the
        difference between them is which SIDE ran out of time, which is not
        cosmetic: `provider_timeout` means the upstream model provider ran out
        of time, while `deadline_exceeded` means COMFY stopped holding the
        connection at its own configured bound. A client that collapses the two
        onto the status alone loses that distinction, and with it the difference
        between "the partner is failing" and "the call is longer than the
        connection Comfy will hold".

        NEITHER BUCKET IS A STATEMENT ABOUT THE CHARGE. Comfy's charges settle
        on COMPLETION: a generation the provider completed is billed whether or
        not the caller was still connected to receive the response, and a
        generation that failed or never completed is not billed. Reaching
        `deadline_exceeded` or `client_disconnected` therefore does not tell a
        caller they were not charged.

        It is deliberately a plain string rather than an `enum`: the set is
        expected to grow -- it already has, and `file_download_error`,
        `cancelled` and `queue_timeout` are named as further additions -- and a
        generated client that hard-rejects an unrecognized bucket would fail
        hardest exactly when something has already gone wrong. Treat an unknown
        value as `internal_error`. Bucketing loses no granularity - the specific
        provider-level reason survives in `RouterValidationErrorDetail.type` and
        its `ctx`.
      example: invalid_input
      type: string
      x-comfy-error-types:
        - meaning: >-
            The request was rejected before it reached the model - a malformed
            body, a malformed or expired pagination cursor, or an input the
            model's own schema does not accept.
          tier: request
          value: invalid_input
        - meaning: >-
            The provider refused the request on content-policy grounds. The
            refusal is deterministic: re-sending the same input will be refused
            again.
          tier: request
          value: content_policy_violation
        - meaning: >-
            The partner provider reported a failure of its own, or returned a
            response Router could not interpret as a result.
          tier: request
          value: provider_error
        - meaning: >-
            The partner provider did not answer within its deadline. This bucket
            is the PROVIDER timing out and never Router's own server deadline,
            which is reported as `deadline_exceeded` - the two share `504` and
            are separated because they name different causes: this one says the
            partner failed, that one says Comfy stopped holding the connection.
          tier: request
          value: provider_timeout
        - meaning: The calling workspace does not have enough credits to run the model.
          tier: request
          value: insufficient_credits
        - meaning: >-
            The `{provider}/{model}` ID names no model Router can run; an
            unknown provider lands here too. `detail` carries up to three
            suggestions drawn from the models the caller is entitled to see.
          tier: request
          value: model_not_found
        - meaning: The request carried no usable credential.
          tier: transport
          value: unauthorized
        - meaning: >-
            The credential is valid but is not entitled to this model or this
            operation.
          tier: transport
          value: forbidden
        - meaning: >-
            The workspace already has as many calls in flight as it is allowed;
            retry once one of them finishes.
          tier: transport
          value: concurrency_limit_exceeded
        - meaning: >-
            The caller closed the connection before Router could return a
            result. It is logged rather than delivered - there is no socket left
            to write it to - and it is an attribution, not a billing outcome: a
            provider generation that completed is billed regardless of whether
            the caller received the response.
          tier: transport
          value: client_disconnected
        - meaning: >-
            Router itself failed. It is also the value a client should treat any
            UNRECOGNIZED bucket as, so a later addition to the set does not
            break a client generated before it.
          tier: transport
          value: internal_error
        - meaning: >-
            Comfy stopped holding the connection at its own configured bound
            before an answer arrived. It shares `504` with `provider_timeout`
            and the pair says which side ran out of time; this one is Comfy's
            own bound, so nothing about the request was rejected and the same
            request may be retried. It says nothing about the charge: a provider
            generation that completed is billed regardless of whether the caller
            received the response. Retry it with the SAME `Idempotency-Key`:
            when the provider had already accepted the generation, the retry
            collects that generation rather than dispatching another, and a
            `Retry-After` on the `504` says when to ask.
          tier: transport
          value: deadline_exceeded
        - meaning: >-
            Comfy Router is not switched on for this caller yet. Nothing about
            the request is wrong and the model exists, which is why this is not
            `model_not_found`; it shares `403` with `forbidden` and is NOT the
            same thing, because `forbidden` is an entitlement decision about the
            caller while this is a state of the rollout. It is TERMINAL: do not
            retry, and do not treat it as an outage.
          tier: transport
          value: not_enabled
        - meaning: >-
            A service Comfy Router depends on is temporarily unavailable and the
            caller did nothing wrong. Retry it with backoff: it is the one
            bucket here whose condition clears on its own, without the caller
            changing the request and without a concurrency slot freeing, which
            is what distinguishes it from the other retryable answers
            (`concurrency_limit_exceeded`, `deadline_exceeded`). It is separate
            from `internal_error` - which is a `500` and means Router itself
            failed - so a client can tell "come back shortly" from "this call is
            not going to work".
          tier: transport
          value: service_unavailable
        - meaning: >-
            The caller has spent an allowance measured over a WINDOW and must
            wait for that window to roll. It shares `429` with
            `concurrency_limit_exceeded` and is not the same thing: that one
            clears the moment one of the caller's own in-flight calls finishes,
            so retrying in seconds is right, whereas nothing the caller does
            drains this one early. `detail` names the window.
          tier: transport
          value: rate_limited
    RouterChargesOnPolicyRejection:
      description: >-
        Whether a call this model REFUSES on content-policy grounds is
        nevertheless charged to the caller. Providers differ, the difference is
        invisible at call time, and a user who sees an error and a charge for
        the same call has no way to have known - so it is stated per model,
        before the call, rather than left to per-provider folklore.

        It pairs with `error_type: content_policy_violation`
        (`RouterErrorType`): that value makes a policy refusal distinguishable,
        this one says what it costs.

        THREE values, and the third is not a formality:

        - `yes` - a policy refusal of this model IS charged. The caller pays for
        a
          generation they did not receive.
        - `no` - a policy refusal of this model is not charged. - `unknown` -
        nobody has established this model's behaviour yet.

        `unknown` exists because the alternative is to default to `no`, and `no`
        is a CLAIM: it tells a caller we do not charge them. Making that the
        default would assert it about every model nobody has checked, and it is
        the expensive direction to be wrong in - it is the answer a user quotes
        back at support. So an unestablished model says `unknown` and means it.

        It is a plain string rather than an `enum`, for the same reason
        `RouterErrorType` and every other extensible Router vocabulary is: a
        generated client that hard-rejects an unrecognized value fails hardest
        on exactly the models it has not been regenerated for. Treat an
        unrecognized value as `unknown`.

        The value is per MODEL and is not derived from the provider: a provider
        can differ across its own operations, and at least one already does, so
        a caller must read it for the model it is about to call rather than for
        the model's provider.
      example: unknown
      type: string
  headers:
    RouterRequestIdHeader:
      description: >-
        Server-generated identifier for this call, present on EVERY Router
        response - success, 4xx and 5xx alike, because an error response is
        exactly when a user needs an id to quote in a support request. The SAME
        value is written into the call's usage/audit event, which is what lets a
        complaint about a charge be joined to the charge itself instead of
        searched for by timestamp.

        It is minted by the server and is never read from a request header of
        the same name: a caller-controlled id would let two unrelated calls
        collide in the audit trail, which would make the join actively
        misleading rather than merely absent. Sending this header on a request
        has no effect.
      required: true
      schema:
        example: 6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21
        format: uuid
        type: string
    RouterErrorTypeHeader:
      description: >-
        Coarse, machine-readable bucket for the failure, set by Router on every
        error response. It carries the same value as
        `RouterErrorResponse.error_type`, and on the `422` it is the ONLY
        machine-readable bucket, because that body is the fal/FastAPI `detail[]`
        shape and has no `error_type` field of its own. A client can therefore
        branch on this header alone, before deciding which of the two Router
        error bodies it received.
      required: true
      schema:
        $ref: '#/components/schemas/RouterErrorType'
  responses:
    RouterRequestError:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RouterErrorResponse'
      description: >-
        A Router request-level failure - the request never reached the model, or
        failed for a reason the model itself did not report. The body is
        `RouterErrorResponse` and the bucket is repeated on
        `X-Comfy-Error-Type`.
      headers:
        X-Comfy-Error-Type:
          $ref: '#/components/headers/RouterErrorTypeHeader'
        X-Comfy-Request-Id:
          $ref: '#/components/headers/RouterRequestIdHeader'
  securitySchemes:
    BearerAuth:
      bearerFormat: JWT
      description: |
        Bearer token authentication. Normally a Firebase or Cloud JWT. A
        'comfyui-' prefixed API key is ALSO accepted here on operations served
        by the comfyFirebase auth middleware: the prefix classifies the value
        as an API key and it is validated exactly as if sent in X-API-Key
        (BE-9720, parity with ingest).
      scheme: bearer
      type: http

````