openapi: 3.0.3
info:
  title: theaccessible.org VPAT Parser API
  description: |
    Public API for parsing VPAT/ACR (Voluntary Product Accessibility Template /
    Accessibility Conformance Report) documents into structured WCAG criteria.

    Submit raw document text (or a URL to fetch) and receive the extracted WCAG
    criteria with conformance levels plus product metadata. Backed by an LLM
    (Gemini, with Anthropic fallback; Bedrock for zero-retention accounts).

    Authentication is via an API key issued by theaccessible.org. Each call
    consumes LLM tokens and is rate-limited per key.
  version: 1.0.0
  contact:
    name: AnglinAI Support
    email: larry@anglin.com
servers:
  - url: https://vpat-parse.larry-c6c.workers.dev
    description: Production (Cloudflare Worker)
security:
  - bearerAuth: []
paths:
  /health:
    get:
      summary: Liveness check
      description: Unauthenticated health probe for uptime monitors and load balancers.
      security: []
      responses:
        '200':
          description: Service is up.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  service:
                    type: string
                    example: vpat-parse
                  version:
                    type: string
                    example: 1.0.0
  /api/v1/vpat-parse:
    post:
      summary: Parse a VPAT/ACR document
      description: |
        Extract WCAG criteria and metadata from a VPAT. Provide exactly one of
        `text` or `url`. Responses use the standard `{ data, error, meta }`
        envelope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ParseRequest'
      responses:
        '200':
          description: Parsed successfully.
          headers:
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining in the current per-key window.
            X-LLM-Provider:
              schema:
                type: string
                enum:
                  - gemini
                  - anthropic
                  - bedrock
            X-LLM-Model:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ParseSuccess'
        '400':
          description: >-
            Invalid JSON, missing `text`/`url`, or a disallowed `url` (SSRF
            guard).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: |
            Either the API key is missing/invalid/revoked (`UNAUTHORIZED`), or a
            fetched source `url` itself returned 401 (`SOURCE_AUTH_REQUIRED`) —
            the `error.code` distinguishes the two.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: A fetched source `url` returned 403 (`SOURCE_AUTH_REQUIRED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '405':
          description: >-
            Method not allowed (`METHOD_NOT_ALLOWED`) — the route accepts POST
            only.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            Zero-retention requested but the zero-retention provider is not
            configured.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '413':
          description: The fetched source body exceeds the size cap.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '415':
          description: The `url` points at a PDF (download and submit it as text instead).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: Per-key rate limit exceeded.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds until the rate-limit window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >
            Server misconfiguration (`CONFIG_ERROR`, e.g. a missing provider
            key)

            or an unexpected internal error (`INTERNAL_ERROR`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '502':
          description: |
            The upstream LLM failed or returned an unparseable response
            (`UPSTREAM_ERROR`), or fetching the source `url` failed
            (`SOURCE_FETCH_FAILED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: |
            Authentication is temporarily unavailable (`AUTH_UNAVAILABLE`) — the
            auth backend is unreachable, or the key is misconfigured (e.g. a
            non-positive rate limit).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key issued by theaccessible.org, sent as `Authorization: Bearer
        <key>`.
  schemas:
    ParseRequest:
      type: object
      description: Provide exactly one of `text` or `url`.
      properties:
        text:
          type: string
          description: Raw extracted text of the VPAT document.
        url:
          type: string
          format: uri
          description: |
            Public http(s) URL of an HTML VPAT to fetch and parse. Validated by
            an SSRF guard (private/internal/metadata targets are rejected) and
            capped in size. PDFs are not supported via URL.
        disableDataRetention:
          type: boolean
          description: Require a zero-retention provider path; fails closed if unavailable.
        bedrockModel:
          type: string
          description: Optional Bedrock model override (allow-listed eval models only).
      example:
        text: 'VPAT 2.4. Product: Acme App. 1.1.1 Non-text Content: Supports.'
    ParseSuccess:
      type: object
      required:
        - data
        - error
        - meta
      properties:
        data:
          $ref: '#/components/schemas/ParseResult'
        error:
          type: object
          nullable: true
          example: null
        meta:
          type: object
          properties:
            model:
              type: string
            provider:
              type: string
              enum:
                - gemini
                - anthropic
                - bedrock
    ParseResult:
      type: object
      properties:
        criteria:
          type: array
          items:
            $ref: '#/components/schemas/Criterion'
        metadata:
          $ref: '#/components/schemas/DocumentMetadata'
        sourceText:
          type: string
          description: >-
            Present only when the request supplied a `url`; the text the LLM
            saw.
    Criterion:
      type: object
      properties:
        id:
          type: string
          description: WCAG criterion number.
          example: 1.1.1
        conformanceLevel:
          type: string
          enum:
            - Supports
            - Partially Supports
            - Does Not Support
            - Not Applicable
            - Not Evaluated
        remarks:
          type: string
          description: The vendor's verbatim remarks for this criterion.
    DocumentMetadata:
      type: object
      properties:
        productName:
          type: string
        productVersion:
          type: string
        vendor:
          type: string
        vpatDate:
          type: string
        vpatVersion:
          type: string
        manualTesting:
          type: boolean
          nullable: true
    ErrorEnvelope:
      type: object
      required:
        - data
        - error
        - meta
      properties:
        data:
          type: object
          nullable: true
          example: null
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
              enum:
                - METHOD_NOT_ALLOWED
                - UNAUTHORIZED
                - RATE_LIMITED
                - AUTH_UNAVAILABLE
                - INVALID_JSON
                - MISSING_FIELD
                - URL_NOT_ALLOWED
                - SOURCE_AUTH_REQUIRED
                - SOURCE_FETCH_FAILED
                - UNSUPPORTED_MEDIA_TYPE
                - SOURCE_TOO_LARGE
                - ZERO_RETENTION_UNAVAILABLE
                - CONFIG_ERROR
                - UPSTREAM_ERROR
                - INTERNAL_ERROR
            message:
              type: string
            details:
              type: string
        meta:
          type: object
          nullable: true
          example: null
