> ## 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.

# VIN Reader

> Tool for reading the VIN of a vehicle.

- Allows you to read the VIN wherever it is stamped or printed on the vehicle.
- Proposes to recover the seventeen characters with a per-character confidence.
- Offers the possibility of validating the check digit before the VIN is accepted.

Reads the windscreen plate, the door jamb sticker, the chassis stamping or the
registration document. Returns the characters — decoding them into a make and model is
a lookup on your side, not something this model does.




## OpenAPI

````yaml /openapi.yaml post /v1/vin/read
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/vin/read:
    post:
      tags:
        - A — Understand the vehicle
      summary: VIN Reader
      description: >
        Tool for reading the VIN of a vehicle.


        - Allows you to read the VIN wherever it is stamped or printed on the
        vehicle.

        - Proposes to recover the seventeen characters with a per-character
        confidence.

        - Offers the possibility of validating the check digit before the VIN is
        accepted.


        Reads the windscreen plate, the door jamb sticker, the chassis stamping
        or the

        registration document. Returns the characters — decoding them into a
        make and model is

        a lookup on your side, not something this model does.
      operationId: readVin
      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/VinReadParams'
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/JsonSingleInput'
                - $ref: '#/components/schemas/VinReadParams'
      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/VinReadJob'
        '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/VinReadJob'
        '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`.
    VinReadParams:
      type: object
      properties:
        validate_check_digit:
          type: boolean
          default: true
          description: Compute the ISO 3779 check digit and report whether it agrees.
        source_hint:
          type: string
          description: Where the VIN sits in the shot, when you know. Narrows the search.
          enum:
            - windscreen
            - door_jamb
            - chassis_stamping
            - engine_bay
            - registration_document
    JsonSingleInput:
      type: object
      required:
        - input_ref
      properties:
        input_ref:
          $ref: '#/components/schemas/UploadRef'
    VinReadJob:
      allOf:
        - $ref: '#/components/schemas/Job'
        - type: object
          properties:
            result:
              oneOf:
                - $ref: '#/components/schemas/VinReadResult'
                - 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
    VinReadResult:
      type: object
      title: vin.read result
      description: >-
        The seventeen characters, where they were read from, and whether the ISO
        3779 check digit agrees with them. Decoding the VIN into a vehicle is a
        lookup on your side.
      required:
        - vin
        - confidence
        - characters
        - check_digit_valid
        - source
        - bbox
      properties:
        vin:
          type:
            - string
            - 'null'
          description: >-
            The seventeen characters, uppercase. Null when no VIN could be read.
            I, O and Q never appear in a valid VIN.
          pattern: ^[A-HJ-NPR-Z0-9]{17}$
          examples:
            - VF3LCYHZPKS123456
        confidence:
          description: Confidence in the whole string. Null when `vin` is null.
          oneOf:
            - $ref: '#/components/schemas/Confidence'
            - type: 'null'
        characters:
          type: array
          description: >-
            Per-character confidence, in reading order. Empty when nothing was
            read. The weak positions are what to show a human for confirmation.
          maxItems: 17
          items:
            $ref: '#/components/schemas/VinCharacter'
        check_digit_valid:
          type:
            - boolean
            - 'null'
          description: >-
            Whether position 9 matches the checksum of the other sixteen. Null
            when the VIN could not be read, or when `validate_check_digit` was
            disabled. A false here on a high-confidence read usually means a
            doctored plate.
        source:
          type:
            - string
            - 'null'
          description: Where on the vehicle the VIN was read.
          enum:
            - windscreen
            - door_jamb
            - chassis_stamping
            - engine_bay
            - registration_document
            - unknown
            - null
        bbox:
          oneOf:
            - $ref: '#/components/schemas/BBox'
            - type: 'null'
    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
    Confidence:
      type: number
      description: Model confidence, 0 to 1. Values below 0.5 warrant a human look.
      minimum: 0
      maximum: 1
      examples:
        - 0.94
    VinCharacter:
      type: object
      required:
        - position
        - value
        - confidence
      properties:
        position:
          type: integer
          minimum: 1
          maximum: 17
        value:
          type: string
          minLength: 1
          maxLength: 1
        confidence:
          $ref: '#/components/schemas/Confidence'
    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
    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
  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`.

````