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

# Estimate a run

> POST /v1/workflows/estimate

A dry run for cost. Nothing executes and nothing is charged.

<Warning>An estimate is not a reservation. Prices and routing can change between the estimate and the run, and the estimate cannot know how many tokens a model will actually emit. Treat it as an order of magnitude.</Warning>


## OpenAPI

````yaml openapi.json POST /v1/workflows/estimate
openapi: 3.0.0
info:
  title: Infery Gateway
  description: >-
    Infery Inference Gateway — OpenAI-compatible API for LLMs, embeddings,
    images, audio, and video
  version: '1.0'
  contact: {}
servers:
  - url: https://api.infery.ai
    description: Production
  - url: http://localhost:3001
    description: Local
security: []
tags: []
paths:
  /v1/workflows/estimate:
    post:
      tags:
        - Workflows
      summary: Dry-run cost estimate for a workflow definition (no execution)
      description: >-
        Free and side-effect-free: no step runs, no credits are held, no run row
        is written and nothing is queued. Supply EXACTLY ONE of `definition` or
        `pipeline_id`; `pipeline_version` is required alongside `pipeline_id`
        (this route does not fall back to the latest version). The definition is
        put through the SAME validator the run path uses, so a 400 here is a
        definition a run would also refuse — which makes this usable as a
        pre-flight syntax check as well as a price check. THE RESULT IS A QUOTE,
        NOT A HOLD AND NOT A CAP: a run settles per step against real usage and
        can land outside this range in either direction. Any step whose row
        carries a "could not be priced" `note` contributed 0 to the totals —
        read `breakdown[].note` before treating `max_credits` as an upper bound.
        Estimating a stored workflow is a READ of it and of every workflow it
        reaches through `sub_pipeline` (the response discloses their step ids,
        types and costs), so it takes the same visibility rules as `GET
        /v1/workflows/{id}`.
      operationId: PipelinesController_estimate[0]
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EstimatePipelineDto'
      responses:
        '201':
          description: The quote, with a per-step breakdown.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EstimateRangeDto'
        '400':
          description: >-
            Neither or both of `definition`/`pipeline_id`; `pipeline_id` without
            `pipeline_version`; a definition the schema or the validator
            refuses; a `sub_pipeline` cycle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '401':
          description: Unauthorized — invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '403':
          description: >-
            The stored workflow references a `sub_pipeline` its author can no
            longer see (`sub_pipeline_no_longer_visible`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '404':
          description: >-
            The `pipeline_id`/`pipeline_version` does not exist, or is not
            visible to this caller — the two are deliberately indistinguishable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
      security:
        - ApiKey: []
components:
  schemas:
    EstimatePipelineDto:
      type: object
      properties:
        definition:
          type: object
          description: Inline workflow definition (mutually exclusive with pipeline_id)
        pipeline_id:
          type: string
          description: Stored workflow UUID (mutually exclusive with definition)
        pipeline_version:
          type: number
          description: Required when pipeline_id is set
        input:
          type: object
          description: >-
            Optional initial input — used for ${input.X} resolution where
            possible
    EstimateRangeDto:
      type: object
      properties:
        min_credits:
          type: number
          description: >-
            Sum of the steps' `min_credits`. The low end of the quote, not a
            floor on what a run will cost.
          example: 2.14
        max_credits:
          type: number
          description: >-
            Sum of the steps' `max_credits`. NOT a cap on billing: a run settles
            per step against actual usage, and any step whose row carries a
            "could not be priced" `note` contributed 0 to this total.
          example: 9.63
        currency:
          type: string
          enum:
            - credits
          description: Always `credits` (1 credit = $0.01). Never dollars.
          example: credits
        breakdown:
          description: >-
            Per-step rows, in definition order. Container steps carry their own
            nested breakdowns, so the array is a tree rather than a flat list.
          type: array
          items:
            $ref: '#/components/schemas/StepEstimateDto'
      required:
        - min_credits
        - max_credits
        - currency
        - breakdown
    ErrorResponseDto:
      type: object
      properties:
        error:
          description: >-
            The error envelope. Every non-2xx response from this API has this
            shape, so a client can parse failures without branching on the
            endpoint.
          allOf:
            - $ref: '#/components/schemas/ErrorDetailDto'
      required:
        - error
    StepEstimateDto:
      type: object
      properties:
        step_id:
          type: string
          description: The step id from the definition.
          example: summarise
        type:
          type: string
          enum:
            - model
            - media
            - three_d
            - capability
            - http
            - parallel
            - sub_pipeline
            - foreach
          description: >-
            Estimate row kind. NOT identical to the definition's `step.type`:
            every capability step — `transform`, `code`, `web.search`,
            `image.resize`, … — comes back as one `capability` row carrying its
            runtime type in `capability_id`.
          example: model
        min_credits:
          type: number
          description: >-
            Low end of the quote for this step, in credits. Floored to 0 for any
            step carrying a `condition`, because the step may not run at all.
            Does not always reconcile against a nested breakdown — see
            `body_steps`.
          example: 0.42
        max_credits:
          type: number
          description: >-
            High end of the quote for this step, in credits. For a `foreach` row
            this is already multiplied by the iteration count. `max_credits ===
            min_credits` does NOT mean the figure is certain — read `note`.
          example: 1.87
        note:
          type: string
          description: >-
            Ambiguity / fallback marker. An OPEN string, not a closed enum —
            treat an unrecognised value as "unknown, do not assume free". The
            values emitted today, and what each means:


            - `unknown_model` — the model slug resolved to nothing. COULD NOT BE
            PRICED: the 0 is an absence of an answer, not a free step.

            - `no_price_configured` — a capability step with no priced row.
            COULD NOT BE PRICED, not free.

            - `sub_pipeline_not_found` — the child workflow does not exist, or
            is in another workspace. COULD NOT BE PRICED.

            - `sub_pipeline_depth_exceeded` — nesting deeper than 3 levels; the
            rest of the chain is unpriced. COULD NOT BE PRICED.

            - `sub_pipeline_estimate_unavailable` — the child lookup failed for
            a reason that is not a clean not-found (the database was
            unreachable). COULD NOT BE PRICED.

            - `sub_pipeline_no_workspace` — a sub-workflow reached costing with
            no workspace to resolve it against. COULD NOT BE PRICED. Not
            reachable through this endpoint today, which always supplies one.

            - `items_count_unknown` — a `foreach` whose `items` is a binding, so
            the iteration count is unknown until the run. PRICED, but the count
            is a ceiling.

            - `params_unknown` — a media step with a binding parameter the
            pricer reads (`n`, `duration`, `size`, `resolution`, `quality`,
            `steps`, TTS `characters`), and every transcription step. PRICED as
            a range — EXCEPT when `min_credits === max_credits`, which marks a
            step that is genuinely UNDER-quoted by an amount the estimator
            cannot state.


            The six marked COULD NOT BE PRICED all report 0 credits. A client
            that renders them as "free" is wrong; render them as unknown.
          example: params_unknown
        model:
          type: string
          description: Model slug being priced. Present for `model` and `media` rows.
          example: gpt-4o
        input_tokens:
          type: number
          description: >-
            Input tokens the quote was computed against, for a `model` row.
            Counted on the UNRESOLVED step — a binding that will carry a large
            upstream result downstream is priced as the literal binding text, so
            this understates such a step.
          example: 1240
        max_output_tokens:
          type: number
          description: The `max_tokens` the quote assumed, for a `model` row.
          example: 1024
        capability_id:
          type: string
          description: >-
            The capability being priced, for a `capability` row — the same
            string as the definition's `step.type` (`code.run.python`,
            `web.search`, `image.resize`, …).
          example: image.resize
        branches:
          description: 'For a `parallel` row: one entry per step of every branch.'
          type: array
          items:
            $ref: '#/components/schemas/StepEstimateDto'
        child_pipeline_id:
          type: string
          description: >-
            For a `sub_pipeline` row: the child workflow, when it was found and
            recursed into.
          example: b0e9f2a4-2b1a-4c7d-9a3e-1f5c8d2e4b60
        child_pipeline_version:
          type: number
          description: 'For a `sub_pipeline` row: the child version that was priced.'
          example: 2
        child_breakdown:
          description: >-
            For a `sub_pipeline` row: the child workflow's own breakdown. Absent
            when the child was not found, is cross-workspace, or exceeded the
            depth limit — the matching `note` says which.
          type: array
          items:
            $ref: '#/components/schemas/StepEstimateDto'
        max_iterations:
          type: number
          description: >-
            For a `foreach` row: the iteration ceiling the quote used (the
            step's `max_iterations`, at most 100).
          example: 100
        items_known:
          type: boolean
          description: >-
            For a `foreach` row: whether `items` is an inline literal array
            (true) or a binding whose length is unknown before the run (false).
          example: false
        items_count:
          type: number
          description: >-
            For a `foreach` row: the literal item count. Present only when
            `items_known` is true.
          example: 4
        body_steps:
          description: >-
            For a `foreach` row: EVERY step of the loop body, in array order,
            each priced for ONE iteration. The row's own `max_credits` is this
            sum times the iteration count. `min_credits` deliberately does NOT
            reconcile against this array when `items` is a binding (min is 0 —
            the loop may run zero times) or when a body step is
            `condition`-gated.
          type: array
          items:
            $ref: '#/components/schemas/StepEstimateDto'
        inner_step:
          description: >-
            For a `foreach` row: literally `body_steps[0]`. Retained because it
            is the published shape and existing clients read it; it is a window
            onto the first body step, not the body. The row's credits are never
            derived from it.
          allOf:
            - $ref: '#/components/schemas/StepEstimateDto'
      required:
        - step_id
        - type
        - min_credits
        - max_credits
    ErrorDetailDto:
      type: object
      properties:
        message:
          type: string
          example: Model not found
          description: Human-readable error message
        type:
          type: string
          example: invalid_request_error
          description: Error category
          enum:
            - invalid_request_error
            - authentication_error
            - permission_error
            - quota_exceeded
            - rate_limit_error
            - server_error
        code:
          type: string
          example: model_not_found
          nullable: true
          description: >-
            Stable machine-readable error code. Branch on this rather than on
            `message`, which is prose and may be reworded.
        param:
          type: string
          example: model
          nullable: true
          description: >-
            Name of the request parameter that triggered the error. `null` when
            the error is not attributable to one field.
        job_id:
          type: string
          example: job_1hR9xTPZqK4mVLc2nJ7fY5wB
          description: >-
            Handle to work that is ALREADY RUNNING AND ALREADY BILLED, present
            on the few errors that carry one. When it is here, this is not a
            failure to retry — retrying pays twice. Collect the result from `GET
            /v1/images/jobs/{job_id}`, which serves every durable media job
            regardless of modality.


            Two situations produce it. A media generation that outruns the
            gateway's wait answers `504` with `code: "job_timeout"` and keeps
            working. And `POST /v1/audio/speech` answers **500** with `code:
            "artifact_unreadable"` when the speech was generated and settled but
            could not be read back from storage — the audio exists and is paid
            for; only this response failed.


            Declared here rather than per-endpoint because the rule is about the
            FIELD, not the status: if this is present, there is a paid-for
            result to collect. It was undeclared until now, so a client
            generated from this document could not see the one field that
            recovers money already spent.
      required:
        - message
        - type
        - code
        - param
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: Authorization
      description: 'API key in format: Bearer inf_***'

````