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

# Car Parts Recognition

> Tool to give a name to each part of your vehicle.

- Allows to recognize the components of a part of a vehicle.
- Proposes to name the parts of a vehicle according to the selected part.
- Offers the possibility of knowing the necessary parts in order to change a part.

Returns a named, boxed part per detection. Part codes are stable identifiers shared by
every endpoint in this API (`front_bumper`, `door_front_left`, `headlight_right`, …).




## OpenAPI

````yaml /openapi.yaml post /v1/parts/recognize
openapi: 3.1.0
info:
  title: CarHub API
  version: '2026-08-01'
  summary: >-
    Automotive AI as a service — 24 inference endpoints, one asynchronous
    contract.
  description: >
    CarHub exposes automotive computer-vision and pricing models as plain HTTP
    endpoints.

    Every model is priced and rate-limited on its own — nothing requires
    adopting the rest.


    ## Two API surfaces


    | Surface | Prefix | Authentication |

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

    | Inference (the product) | `/v1/…` | API key — `Authorization: Bearer
    chk_live_…` / `chk_test_…` |

    | Management (the dashboard) | `/mgmt/v1/…` | Short-lived JWT —
    `Authorization: Bearer <jwt>` |


    ## Everything is a job


    Every inference endpoint is `POST` and **asynchronous by default**: it
    answers `202` with a

    [`Job`](#/components/schemas/Job) in status `queued`. Collect the result by
    polling

    `GET /v1/jobs/{id}` or by subscribing to the `job.succeeded` / `job.failed`
    webhooks.


    Fast endpoints additionally accept `?wait=true`, which holds the connection
    until the job

    settles (30 s ceiling) and answers `200` with the same `Job` object in
    status `succeeded`.

    Video, 360 and bundle endpoints reject `wait` with `400 wait_not_supported`
    — their work

    outlives any reasonable request timeout.


    ## Inputs


    Every endpoint accepts two request encodings for the same operation:


    * `multipart/form-data` — the binary directly in the request (`image`,
    `audio`, `video` or
      `images[]` depending on the endpoint). Simplest path, capped at 10 MB per file.
    * `application/json` — `{"input_ref": "upl_…"}` (or `input_refs` for
    multi-asset endpoints)
      referencing an upload created through `POST /v1/uploads`. Required for videos and batches.

    Endpoint parameters are identical in both encodings.


    ## Idempotency


    Send `Idempotency-Key` on any `POST`. A replayed key returns the original
    response and never

    bills twice; reusing a key with a different payload is a `409
    idempotency_key_reuse`.


    ## Test mode


    Keys prefixed `chk_test_` run the entire pipeline — auth, rate limiting, job
    lifecycle,

    ledger, signed webhooks — against a fixture backend instead of a GPU. Test
    jobs carry

    `livemode: false` and draw on a separate, freely rechargeable test credit
    balance.

    Drive the fixture backend per request with `X-Carhub-Test-Scenario`.
  termsOfService: https://trycarhub.com/terms
  contact:
    name: CarHub API support
    url: https://docs.trycarhub.com
    email: api@trycarhub.com
  license:
    name: Proprietary
    identifier: LicenseRef-CarHub-Commercial
servers:
  - url: https://api.trycarhub.com
    description: Production (EU)
security:
  - ApiKeyAuth: []
tags:
  - name: A — Understand the vehicle
    description: >-
      Identity, attributes and a clean capture — everything downstream reads
      from here.
  - name: B — Damage & condition
    description: Find it, then grade it, so your own rules can act on the result.
  - name: C — Parts
    description: >-
      Turns "there is damage" into "there is damage on this component" — what
      makes every downstream estimate defensible.
  - name: D — Money
    description: Valuation and repair cost, computed from what was actually observed.
  - name: E — Image production
    description: >-
      Exposed as raw infrastructure, not a batch retouching service. Call it per
      asset, in your own pipeline.
  - name: Inspections
    description: The eight-stage bundle, billed as a single blended inspection.
  - name: Jobs
    description: Status, results and history of asynchronous work.
  - name: Uploads
    description: Presigned storage for videos and batches.
  - name: Management
    description: >-
      Organisation, API keys, usage, billing and outgoing webhooks. JWT
      authenticated.
  - name: Internal
    description: Inbound callbacks from the inference plane. Never called by customers.
externalDocs:
  description: CarHub developer documentation
  url: https://docs.trycarhub.com
paths:
  /v1/parts/recognize:
    post:
      tags:
        - C — Parts
      summary: Car Parts Recognition
      description: >
        Tool to give a name to each part of your vehicle.


        - Allows to recognize the components of a part of a vehicle.

        - Proposes to name the parts of a vehicle according to the selected
        part.

        - Offers the possibility of knowing the necessary parts in order to
        change a part.


        Returns a named, boxed part per detection. Part codes are stable
        identifiers shared by

        every endpoint in this API (`front_bumper`, `door_front_left`,
        `headlight_right`, …).
      operationId: recognizeParts
      parameters:
        - $ref: '#/components/parameters/Wait'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/TestScenario'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              allOf:
                - $ref: '#/components/schemas/MultipartImageInput'
                - $ref: '#/components/schemas/PartsRecognizeParams'
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/JsonSingleInput'
                - $ref: '#/components/schemas/PartsRecognizeParams'
      responses:
        '200':
          description: Job completed synchronously (`wait=true`).
          headers:
            Request-Id:
              $ref: '#/components/headers/RequestId'
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartsRecognizeJob'
        '202':
          description: Job accepted, credits reserved, work queued.
          headers:
            Request-Id:
              $ref: '#/components/headers/RequestId'
            Location:
              $ref: '#/components/headers/JobLocation'
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartsRecognizeJob'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    Wait:
      name: wait
      in: query
      required: false
      description: >
        Hold the connection until the job settles and answer `200` with the
        completed job instead

        of `202` with a queued one. Available on fast endpoints only; the
        ceiling is 30 s, after

        which the job keeps running and the call answers `202` as usual.
      schema:
        type: boolean
        default: false
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >
        Client-generated key (a UUID is ideal) making this `POST` safe to retry.
        The first request

        is executed and its response stored for 24 h; any replay with the same
        key returns that

        stored response with `Idempotent-Replayed: true` and bills nothing.
        Reusing a key with a

        different payload is a `409 idempotency_key_reuse`.
      schema:
        type: string
        minLength: 8
        maxLength: 255
      examples:
        uuid:
          value: 8b1f1c2e-9d3a-4a51-9f0c-1c9a2f6d7b44
    TestScenario:
      name: X-Carhub-Test-Scenario
      in: header
      required: false
      description: >
        Steers the fixture backend. Ignored by live keys, `400
        test_scenario_not_supported` if the

        scenario is unknown to the endpoint. Common catalogue:


        | Scenario | Effect |

        |---|---|

        | *(absent)* | Deterministic success — the endpoint's `default` fixture.
        |

        | `empty_result` | Success with nothing detected: empty lists, null
        readings. |

        | `low_confidence` | Success with every confidence below 0.5. |

        | `model_error` | Job settles `failed`; the reservation is released. |

        | `slow` | Completion after ~30 s — makes `wait=true` time out. |

        | `insufficient_credits` | `402` at admission, no job created. |


        Some endpoints add their own scenarios; unsupported common scenarios
        (for example

        `empty_result` on a generation endpoint) are rejected rather than
        silently ignored.
      schema:
        type: string
        examples:
          - empty_result
          - low_confidence
          - model_error
          - slow
          - insufficient_credits
  schemas:
    MultipartImageInput:
      type: object
      required:
        - image
      properties:
        image:
          type: string
          format: binary
          description: >-
            JPEG, PNG or WebP, 10 MB maximum. Larger files must go through
            `/v1/uploads`.
    PartsRecognizeParams:
      type: object
      properties:
        scope:
          type: string
          enum:
            - exterior
            - interior
          default: exterior
        min_confidence:
          type: number
          minimum: 0
          maximum: 1
          default: 0.3
    JsonSingleInput:
      type: object
      required:
        - input_ref
      properties:
        input_ref:
          $ref: '#/components/schemas/UploadRef'
    PartsRecognizeJob:
      allOf:
        - $ref: '#/components/schemas/Job'
        - type: object
          properties:
            result:
              oneOf:
                - $ref: '#/components/schemas/PartsRecognizeResult'
                - type: 'null'
    UploadRef:
      type: string
      description: Reference returned by `POST /v1/uploads`.
      pattern: ^upl_[1-9A-HJ-NP-Za-km-z]{8,32}$
      examples:
        - upl_7Qm2hV9tXbLd
    Job:
      type: object
      title: Job
      description: >
        The unit of work of the API. Every inference call creates one, and every
        result is read

        back from one — synchronously through `wait=true`, or later through `GET
        /v1/jobs/{id}`

        or a webhook.


        `result` is populated only in `succeeded`, `error` only in `failed` /
        `expired`.

        `stages` is present on inspection jobs only.
      required:
        - id
        - object
        - endpoint
        - status
        - livemode
        - created
        - started_at
        - completed_at
        - model_version
        - credits
        - result
        - error
      properties:
        id:
          $ref: '#/components/schemas/JobIdString'
        object:
          type: string
          const: job
        endpoint:
          $ref: '#/components/schemas/EndpointSlug'
        status:
          $ref: '#/components/schemas/JobStatus'
        livemode:
          type: boolean
          description: >-
            `false` when the job was created with a `chk_test_` key: fixture
            result, test credits.
        created:
          $ref: '#/components/schemas/Timestamp'
        started_at:
          description: When the job left the queue. Null while `queued`.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        completed_at:
          description: When the job settled. Null until then.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        model_version:
          type:
            - string
            - 'null'
          description: >-
            Exact model build that produced the result, as `<model>@<release
            date>`. Null until the job starts. Pin your regression tests to it.
          examples:
            - plate-reader@2026-08-01
        credits:
          $ref: '#/components/schemas/JobCredits'
        result:
          description: >-
            Endpoint-specific payload, null until the job succeeds. See each
            endpoint's `200` / `202` schema for its exact shape.
          oneOf:
            - type: object
            - type: 'null'
        error:
          description: Populated when `status` is `failed` or `expired`, null otherwise.
          oneOf:
            - $ref: '#/components/schemas/JobError'
            - type: 'null'
        stages:
          type: array
          description: >-
            Per-stage progress. Present on `inspections` jobs only, absent
            everywhere else.
          items:
            $ref: '#/components/schemas/JobStage'
        test_scenario:
          type: string
          description: >-
            The `X-Carhub-Test-Scenario` that produced this job. Present on
            test-mode jobs that were steered by one, absent otherwise — a live
            job never carries it.
          examples:
            - low_confidence
    PartsRecognizeResult:
      type: object
      title: parts.recognize result
      description: >-
        Every vehicle part visible in the frame, named and boxed. The codes are
        the ones every other endpoint uses to talk about components.
      required:
        - image
        - scope
        - parts
      properties:
        image:
          $ref: '#/components/schemas/ImageSize'
        scope:
          type: string
          description: Which taxonomy was applied.
          enum:
            - exterior
            - interior
        parts:
          type: array
          description: One entry per recognised part, most confident first.
          items:
            $ref: '#/components/schemas/RecognizedPart'
    ErrorResponse:
      type: object
      title: Error response
      description: >-
        The single error envelope of the API. Every non-2xx response on every
        route group has exactly this shape.
      required:
        - error
      additionalProperties: false
      properties:
        error:
          $ref: '#/components/schemas/Error'
    JobIdString:
      type: string
      description: Job identifier — `job_` followed by a base58 string.
      pattern: ^job_[1-9A-HJ-NP-Za-km-z]{8,32}$
      examples:
        - job_3ZxK9pQr7TnW
    EndpointSlug:
      type: string
      description: >-
        Dotted slug of a CarHub endpoint, the identifier used for jobs and
        usage.
      enum:
        - vehicle.analyze
        - vehicle.segment
        - vehicle.identify
        - vin.read
        - plate.read
        - document.read
        - dashboard.read
        - engine.detect
        - video.onboard
        - damage.detect
        - damage.severity
        - tire.read
        - tire.detect
        - parts.recognize
        - parts.segment
        - parts.decompose
        - pricing.vehicle
        - pricing.repair
        - render.background
        - render.enhance
        - render.plate
        - render.360
        - render.video
        - viewer.hotspots
        - inspections
    JobStatus:
      type: string
      description: >
        Lifecycle of a job. `queued` → `processing` → one of `succeeded` |
        `failed`; `expired`

        when it was never picked up in time, `canceled` when it was cancelled
        before starting.

        The three settled states are terminal.
      enum:
        - queued
        - processing
        - succeeded
        - failed
        - expired
        - canceled
    Timestamp:
      type: integer
      format: int64
      description: Epoch seconds, UTC.
      examples:
        - 1755300000
    JobCredits:
      type: object
      description: >
        Credit accounting for the job. One credit equals one USD cent.
        `reserved` is held at

        admission and `charged` is the real cost after settlement; unused
        credits are released.
      required:
        - reserved
        - charged
      properties:
        reserved:
          type: integer
          minimum: 0
          examples:
            - 20
        charged:
          type:
            - integer
            - 'null'
          minimum: 0
          description: Null while the job is unsettled.
          examples:
            - 20
    JobError:
      type: object
      description: Why a job settled in `failed` or `expired`.
      required:
        - type
        - code
        - message
      properties:
        type:
          type: string
          enum:
            - processing_error
            - invalid_request
            - internal
        code:
          type: string
          examples:
            - model_inference_failed
            - unreadable_input
            - upstream_timeout
            - job_expired
        message:
          type: string
    JobStage:
      type: object
      title: Inspection stage
      description: >
        One of the eight stages of an inspection. Each stage is a real child job
        running on the

        same queue and the same workers as a direct call, so `job_id` is
        readable on its own

        through `GET /v1/jobs/{id}` and carries that stage's full,
        endpoint-specific result.
      required:
        - stage
        - endpoint
        - name
        - status
        - started_at
        - completed_at
        - job_id
        - error
      properties:
        stage:
          type: integer
          minimum: 1
          maximum: 8
          description: Position in the pipeline, 1-based.
        endpoint:
          allOf:
            - $ref: '#/components/schemas/EndpointSlug'
          description: >-
            The endpoint this stage runs — the same one you could have called
            directly.
        name:
          type: string
          description: >-
            Stable name of the stage, independent of which endpoint currently
            implements it — branch on this rather than on `endpoint` if you want
            your UI to survive a pipeline change.
          enum:
            - onboard_walkaround
            - isolate_vehicle
            - decompose_parts
            - detect_damage
            - grade_severity
            - price_repair
            - render_listing
            - anchor_hotspots
        model:
          type: string
          description: Model backing this stage.
          examples:
            - video-onboarding
            - car-segmentation
            - damage-detection
        status:
          $ref: '#/components/schemas/JobStatus'
        started_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        completed_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        job_id:
          description: Child job that ran the stage. Null before the stage starts.
          oneOf:
            - $ref: '#/components/schemas/JobIdString'
            - type: 'null'
        error:
          oneOf:
            - $ref: '#/components/schemas/JobError'
            - type: 'null'
        result:
          description: >-
            The stage's own result, inlined once it has succeeded. Same shape as
            the result of the endpoint named in `endpoint`. Absent while the
            stage is unsettled.
          type: object
        skipped:
          type: boolean
          description: >-
            True when the stage was excluded by the request's `stages`
            parameter.
          default: false
    ImageSize:
      type: object
      description: >-
        Pixel dimensions of the submitted image, echoed so coordinates can be
        scaled.
      required:
        - width
        - height
      properties:
        width:
          type: integer
          minimum: 1
        height:
          type: integer
          minimum: 1
    RecognizedPart:
      type: object
      required:
        - id
        - code
        - label
        - side
        - bbox
        - confidence
      properties:
        id:
          type: string
          examples:
            - prt_1
        code:
          $ref: '#/components/schemas/PartCode'
        label:
          type: string
          examples:
            - Front bumper
        side:
          type:
            - string
            - 'null'
          description: Null for parts a vehicle has only one of.
          enum:
            - left
            - right
            - center
            - null
        bbox:
          $ref: '#/components/schemas/BBox'
        confidence:
          $ref: '#/components/schemas/Confidence'
    Error:
      type: object
      required:
        - type
        - code
        - message
        - param
        - doc_url
        - request_id
      properties:
        type:
          type: string
          description: >-
            Broad class of the failure — enough to branch on without matching
            codes.
          enum:
            - invalid_request
            - authentication
            - permission
            - rate_limit
            - insufficient_credits
            - not_found
            - conflict
            - processing_error
            - internal
        code:
          type: string
          description: >-
            Stable machine-readable identifier, narrower than `type`. New codes
            may appear; treat an unknown code as its `type`.
          examples:
            - missing_input
            - invalid_api_key
            - rate_limit_exceeded
        message:
          type: string
          description: >-
            English, human-readable, safe to log. Never localised, never for end
            users.
        param:
          type:
            - string
            - 'null'
          description: >-
            The offending field or header, when the failure can be attributed to
            one.
        doc_url:
          type: string
          format: uri
          description: Documentation page for this exact code.
          examples:
            - https://docs.trycarhub.com/errors/missing_input
        request_id:
          type: string
          description: >-
            Identifier of the failing request, mirrored in the `Request-Id`
            header.
          examples:
            - req_2Kd8xQmPvL4T
    PartCode:
      type: string
      description: >
        Stable identifier of a vehicle part, shared by every endpoint that names
        one. Snake case,

        side-suffixed where a vehicle has two (`door_front_left`). The list
        grows with the part

        taxonomy, so treat an unknown code as opaque rather than rejecting it.
      examples:
        - front_bumper
        - rear_bumper
        - bonnet
        - tailgate
        - roof
        - windscreen
        - grille
        - door_front_left
        - door_rear_right
        - wing_front_left
        - quarter_panel_rear_right
        - headlight_left
        - taillight_right
        - mirror_left
        - wheel_front_left
        - sill_left
        - dashboard
        - seat_front_left
    BBox:
      type: object
      title: Bounding box
      description: >-
        Axis-aligned box in pixels of the submitted image, origin at the
        top-left corner.
      required:
        - x
        - 'y'
        - width
        - height
      properties:
        x:
          type: integer
          description: Left edge, in pixels.
        'y':
          type: integer
          description: Top edge, in pixels.
        width:
          type: integer
          minimum: 0
          description: Width, in pixels.
        height:
          type: integer
          minimum: 0
          description: Height, in pixels.
      examples:
        - x: 120
          'y': 340
          width: 180
          height: 48
    Confidence:
      type: number
      description: Model confidence, 0 to 1. Values below 0.5 warrant a human look.
      minimum: 0
      maximum: 1
      examples:
        - 0.94
  headers:
    RequestId:
      description: Unique identifier of this request. Quote it in any support conversation.
      schema:
        type: string
        examples:
          - req_2Kd8xQmPvL4T
    IdempotentReplayed:
      description: >-
        `true` when this response was replayed from a previous request carrying
        the same `Idempotency-Key`. Absent when the request was executed for
        real.
      schema:
        type: boolean
    JobLocation:
      description: Absolute URL of the created job.
      schema:
        type: string
        format: uri
        examples:
          - https://api.trycarhub.com/v1/jobs/job_3ZxK9pQr7TnW
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
        examples:
          - 12
    RateLimitLimit:
      description: Requests allowed in the current window for this endpoint and plan.
      schema:
        type: integer
        examples:
          - 120
    RateLimitRemaining:
      description: Requests left in the current window.
      schema:
        type: integer
        examples:
          - 0
    RateLimitReset:
      description: Epoch second at which the current window resets.
      schema:
        type: integer
        examples:
          - 1755300060
  responses:
    BadRequest:
      description: >
        Malformed or unusable request — bad JSON, missing or oversized file,
        unknown parameter,

        `wait=true` on an endpoint that does not support it.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missingInput:
              summary: No input supplied
              value:
                error:
                  type: invalid_request
                  code: missing_input
                  message: Provide either a multipart `image` field or an `input_ref`.
                  param: image
                  doc_url: https://docs.trycarhub.com/errors/missing_input
                  request_id: req_2Kd8xQmPvL4T
            waitUnsupported:
              summary: wait=true on a long-running endpoint
              value:
                error:
                  type: invalid_request
                  code: wait_not_supported
                  message: >-
                    This endpoint is long-running; poll the job or use a webhook
                    instead.
                  param: wait
                  doc_url: https://docs.trycarhub.com/errors/wait_not_supported
                  request_id: req_2Kd8xQmPvL4T
    Unauthorized:
      description: Missing, malformed, revoked or expired credential.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalidKey:
              value:
                error:
                  type: authentication
                  code: invalid_api_key
                  message: The provided API key is invalid or has been revoked.
                  param: null
                  doc_url: https://docs.trycarhub.com/errors/invalid_api_key
                  request_id: req_2Kd8xQmPvL4T
    InsufficientCredits:
      description: >
        The organisation's balance cannot cover the reservation for this call.
        Nothing was

        queued and nothing was charged. Top up with `POST
        /mgmt/v1/billing/checkout` — or, in

        test mode, `POST /mgmt/v1/billing/test-credits`.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            emptyCreditBalances:
              value:
                error:
                  type: insufficient_credits
                  code: insufficient_credits
                  message: Credit balance is 3 minor units; this call reserves 8.
                  param: null
                  doc_url: https://docs.trycarhub.com/errors/insufficient_credits
                  request_id: req_2Kd8xQmPvL4T
    Forbidden:
      description: >-
        Authenticated but not allowed: key scope excludes this endpoint family,
        role too low for a management route, or the resource belongs to another
        organisation.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            outOfScope:
              value:
                error:
                  type: permission
                  code: scope_not_granted
                  message: This API key is not scoped for the `render` endpoint family.
                  param: null
                  doc_url: https://docs.trycarhub.com/errors/scope_not_granted
                  request_id: req_2Kd8xQmPvL4T
    NotFound:
      description: No such resource, or it is not visible to this credential.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            unknownJob:
              value:
                error:
                  type: not_found
                  code: job_not_found
                  message: No job with id `job_3ZxK9pQr7TnW`.
                  param: id
                  doc_url: https://docs.trycarhub.com/errors/job_not_found
                  request_id: req_2Kd8xQmPvL4T
    Conflict:
      description: >-
        The request contradicts the current state — an idempotency key replayed
        with a different body, or an upload reference already consumed.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            idempotencyReuse:
              value:
                error:
                  type: conflict
                  code: idempotency_key_reuse
                  message: >-
                    This Idempotency-Key was already used with a different
                    request body.
                  param: Idempotency-Key
                  doc_url: https://docs.trycarhub.com/errors/idempotency_key_reuse
                  request_id: req_2Kd8xQmPvL4T
    RateLimited:
      description: Too many requests for this endpoint and plan. Back off and retry.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            tooFast:
              value:
                error:
                  type: rate_limit
                  code: rate_limit_exceeded
                  message: >-
                    Rate limit of 120 requests per minute exceeded for
                    `plate.read`.
                  param: null
                  doc_url: https://docs.trycarhub.com/errors/rate_limit_exceeded
                  request_id: req_2Kd8xQmPvL4T
    InternalError:
      description: >-
        Something broke on our side. The request was not billed. Retry with the
        same `Idempotency-Key`; if it persists, quote `request_id`.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            internal:
              value:
                error:
                  type: internal
                  code: internal_error
                  message: An unexpected error occurred.
                  param: null
                  doc_url: https://docs.trycarhub.com/errors/internal_error
                  request_id: req_2Kd8xQmPvL4T
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: CarHub API key
      description: >
        Organisation API key, sent as `Authorization: Bearer chk_live_…`.


        * `chk_live_…` — real inference, real credits, `livemode: true`.

        * `chk_test_…` — identical pipeline against the fixture backend, test
        credits,
          `livemode: false`.

        Keys are stored hashed; only the prefix (`chk_live_a1b2…`) is ever
        displayed again. A key

        may be scoped to a subset of endpoint families — calling outside its
        scopes is a `403`.

````