openapi: 3.1.0
info:
  title: VINState API
  version: 1.0.0
  description: Production V1 API for authenticated single VIN decoding.
servers:
  - url: https://api.vinstate.com
paths:
  /api/ping:
    get:
      summary: API health ping
      operationId: getApiPing
      responses:
        '200':
          description: API is reachable.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    const: true
                  message:
                    type: string
  /api/webhooks/creem:
    post:
      summary: Creem payment webhook endpoint
      operationId: postCreemWebhook
      parameters:
        - in: header
          name: creem-signature
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Webhook was processed or already processed.
        '400':
          $ref: '#/components/responses/ApiError'
        '401':
          $ref: '#/components/responses/ApiError'
        '500':
          $ref: '#/components/responses/ApiError'
  /api/v1/vin/{vin}:
    get:
      summary: Decode a single VIN
      operationId: getVinLookup
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: vin
          required: true
          description: Raw VIN value from the request path. The API trims and uppercases it before validation.
          schema:
            type: string
            minLength: 1
            maxLength: 64
        - in: header
          name: X-Request-Id
          required: false
          description: Optional UUID echoed in the response and used as the request identifier.
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: VIN lookup succeeded and billing was finalized.
          headers:
            X-Request-Id:
              schema:
                type: string
                format: uuid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VinLookupSuccessResponse'
        '401':
          $ref: '#/components/responses/ApiError'
        '402':
          $ref: '#/components/responses/VinLookupError'
        '403':
          $ref: '#/components/responses/ApiError'
        '404':
          $ref: '#/components/responses/VinLookupError'
        '422':
          $ref: '#/components/responses/VinLookupError'
        '429':
          $ref: '#/components/responses/ApiError'
        '500':
          $ref: '#/components/responses/VinLookupError'
        '503':
          $ref: '#/components/responses/VinLookupError'
components:
  responses:
    ApiError:
      description: Request failed before a successful VIN lookup could be billed.
      headers:
        X-Request-Id:
          schema:
            type: string
            format: uuid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiErrorResponse'
    VinLookupError:
      description: VIN lookup failed and was not billed.
      headers:
        X-Request-Id:
          schema:
            type: string
            format: uuid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/VinLookupErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: Send your API key as Authorization: Bearer sk-your-api-key
  schemas:
    ApiError:
      type: object
      properties:
        code:
          type: string
          examples:
            - missing_api_key
            - invalid_api_key
            - rate_limit_exceeded
        message:
          type: string
        status:
          type: integer
        details:
          oneOf:
            - type: object
              additionalProperties: true
            - type: array
              items: {}
      required:
        - code
        - message
        - status
        - details
    ApiErrorMeta:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/ApiError'
      required:
        - error
    ApiErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          const: false
        request_id:
          type: string
          format: uuid
        data:
          type: 'null'
        meta:
          $ref: '#/components/schemas/ApiErrorMeta'
      required:
        - success
        - request_id
        - data
        - meta
    VinLookupDecodedSection:
      type: object
      additionalProperties: true
    VinLookupDecodedPayload:
      type: object
      properties:
        vin:
          type: string
          example: 1HGCM82633A004352
        decoded:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/VinLookupDecodedSection'
      required:
        - vin
        - decoded
    VinLookupMeta:
      type: object
      properties:
        billed:
          type: boolean
        funding_source:
          type: string
          nullable: true
          enum: [included, credits]
        included_requests_charged:
          type: integer
        charged_credits:
          type: integer
      required:
        - billed
    VinLookupErrorMeta:
      allOf:
        - $ref: '#/components/schemas/ApiErrorMeta'
        - type: object
          properties:
            vin:
              type: string
            validation_errors:
              type: array
              items:
                type: string
            billed:
              type: boolean
          required:
            - billed
            - error
    VinLookupSuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        request_id:
          type: string
          format: uuid
        data:
          type: array
          items:
            $ref: '#/components/schemas/VinLookupDecodedPayload'
        meta:
          $ref: '#/components/schemas/VinLookupMeta'
      required:
        - success
        - request_id
        - data
        - meta
    VinLookupErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          const: false
        request_id:
          type: string
          format: uuid
        data:
          type: 'null'
        meta:
          $ref: '#/components/schemas/VinLookupErrorMeta'
      required:
        - success
        - request_id
        - data
        - meta
