openapi: 3.1.0
info:
  title: Diffchecker API
  version: 1.0.0
  description: |
    The Diffchecker API provides HTTP endpoints for comparing text, PDF documents, PDF rich text data, images, and Excel spreadsheets. Compute text diffs, document comparisons, structured rich-text diffs, image diffs, and spreadsheet diffs, all with varying output types.

    ## Authentication

    There are two ways of interacting with the Diffchecker API:

    - **Email**: Just pass your email as a query string parameter. This method does not require you to have a Diffchecker account and will allow you to make as many diffs as free tier limits allow.
    - **API Key**: Every paid subscriber gets their own API key, which needs to be passed as the request's `X-Api-Key` header. This will allow you to make as many diffs as your paid plan allows.

    When both are provided, email gets ignored in favor of the API key. The examples in these docs assume you are authenticating via email.

    ## Rate Limiting

    API requests are rate-limited based on your authentication method. Free tier (email) users have lower limits than paid subscribers using an API key. If you exceed the rate limit, the API will return a `429 Too Many Requests` response.

    Every public API response includes an `X-Credits-Used` response header. For backwards compatibility, JSON responses also continue to include the same value in the `creditsUsed` response body field.

    Some `429` responses are returned for exhausted free or paid diff quotas. Zero-credit failed requests are also throttled separately and may include a `retryAfterSeconds` field in the JSON body.

    ## Resources

    You may find the following resources helpful when dealing with PDF, Image, or Excel diffs:

    - [Data URLs (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs)
    - [FormData (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/FormData)

servers:
  - url: https://api.diffchecker.com/public
    description: Production

security:
  - apiKey: []
  - email: []

paths:
  /auth-test:
    get:
      operationId: authTest
      summary: Test authentication
      description: Verify that your authentication credentials are working correctly.
      parameters:
        - $ref: '#/components/parameters/email'
      responses:
        '200':
          description: Authentication is working correctly.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                type: object
                properties:
                  creditsUsed:
                    type: integer
                    example: 0
                  message:
                    type: string
                    example: Auth is working correctly.
                required: [creditsUsed, message]
              example:
                creditsUsed: 0
                message: Auth is working correctly.
        '401':
          $ref: '#/components/responses/Unauthorized'

  /text:
    post:
      operationId: textDiff
      summary: Compare Text
      description: Compute a diff between two text strings with configurable output formats.
      parameters:
        - $ref: '#/components/parameters/email'
        - name: output_type
          in: query
          required: true
          description: |
            Specifies the type of output you receive in the response body.
            - `json`: Row metadata generated from diff computation (Content-Type: application/json)
            - `html`: Same HTML/CSS you see on the Diffchecker site (Content-Type: text/html)
            - `html_json`: Same HTML/CSS you see on the Diffchecker site, but split up and embedded in JSON (Content-Type: application/json)
          schema:
            type: string
            enum: [json, html, html_json]
        - name: diff_level
          in: query
          required: false
          description: "Specifies whether you want to diff by word or character. Default is `word`."
          schema:
            type: string
            enum: [word, character]
            default: word
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [left, right]
              properties:
                left:
                  type: string
                  description: Left text you want to diff.
                right:
                  type: string
                  description: Right text you want to diff.
            example:
              left: "roses are red\nviolets are blue"
              right: "roses are green\nviolets are purple"
      responses:
        '200':
          description: Diff computed successfully.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TextDiffJsonResponse'
                  - $ref: '#/components/schemas/HtmlJsonResponse'
              examples:
                json:
                  summary: output_type=json
                  value:
                    creditsUsed: 1
                    rows:
                      - end: false
                        left:
                          chunks:
                            - value: "roses are "
                              type: equal
                            - value: red
                              type: remove
                          line: 1
                        right:
                          chunks:
                            - value: "roses are "
                              type: equal
                            - value: green
                              type: insert
                          line: 1
                        insideChanged: true
                        start: true
                      - end: true
                        left:
                          chunks:
                            - value: ""
                              type: remove
                            - value: "violets are "
                              type: equal
                            - value: blue
                              type: remove
                          line: 2
                        right:
                          chunks:
                            - value: ""
                              type: insert
                            - value: "violets are "
                              type: equal
                            - value: purple
                              type: insert
                          line: 2
                        insideChanged: true
                    added: 3
                    removed: 3
                html_json:
                  summary: output_type=html_json
                  value:
                    creditsUsed: 1
                    html: '<table class="diff-table">...</table>'
                    css: '.diff-table { font-family: monospace; ... }'
            text/html:
              schema:
                type: string
                description: Full HTML with embedded CSS for the diff table.
        '400':
          $ref: '#/components/responses/ValidationError'
        '413':
          $ref: '#/components/responses/RequestTooLarge'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'

  /pdf/plaintext:
    post:
      operationId: documentDiff
      summary: Compare Documents (Plain Text PDF)
      description: |
        Compute a plain-text diff between two PDF documents. Supports both multipart/form-data file uploads and JSON with base64-encoded data URLs.
      parameters:
        - $ref: '#/components/parameters/email'
        - name: input_type
          in: query
          required: false
          description: |
            Specifies the request content-type.
            - `form`: multipart/form-data (default)
            - `json`: application/json
          schema:
            type: string
            enum: [json, form]
            default: form
        - name: output_type
          in: query
          required: true
          description: |
            Specifies the type of output you receive in the response body.
            - `json`: Row metadata generated from PDF-text diff computation (Content-Type: application/json)
            - `html`: Same HTML/CSS you see on the Diffchecker site for PDF-text diffs (Content-Type: text/html)
            - `html_json`: Same HTML/CSS, but split up and embedded in JSON (Content-Type: application/json)
          schema:
            type: string
            enum: [json, html, html_json]
        - name: diff_level
          in: query
          required: false
          description: "Specifies whether you want to diff by word or character. Default is `word`. Should only be used with PDF-text diff related output types."
          schema:
            type: string
            enum: [word, character]
            default: word
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [left_pdf, right_pdf]
              properties:
                left_pdf:
                  type: string
                  format: binary
                  description: Left PDF file you want to diff. File extension must be .pdf.
                right_pdf:
                  type: string
                  format: binary
                  description: Right PDF file you want to diff. File extension must be .pdf.
          application/json:
            schema:
              type: object
              required: [left_pdf, right_pdf]
              properties:
                left_pdf:
                  type: string
                  description: Data URL of the left PDF (data:application/pdf;base64,...).
                right_pdf:
                  type: string
                  description: Data URL of the right PDF (data:application/pdf;base64,...).
      responses:
        '200':
          description: Diff computed successfully. Same response structure as Text diff.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TextDiffJsonResponse'
                  - $ref: '#/components/schemas/HtmlJsonResponse'
              examples:
                json:
                  summary: output_type=json
                  value:
                    creditsUsed: 1
                    rows:
                      - end: false
                        left:
                          chunks:
                            - value: "The quick brown fox "
                              type: equal
                            - value: jumps
                              type: remove
                          line: 1
                        right:
                          chunks:
                            - value: "The quick brown fox "
                              type: equal
                            - value: leaps
                              type: insert
                          line: 1
                        insideChanged: true
                        start: true
                      - end: true
                        left:
                          chunks:
                            - value: ""
                              type: remove
                            - value: "over the lazy dog."
                              type: equal
                          line: 2
                        right:
                          chunks:
                            - value: ""
                              type: insert
                            - value: "over the lazy dog."
                              type: equal
                          line: 2
                        insideChanged: false
                    added: 1
                    removed: 1
                html_json:
                  summary: output_type=html_json
                  value:
                    creditsUsed: 1
                    html: '<table class="diff-table">...</table>'
                    css: '.diff-table { font-family: monospace; ... }'
            text/html:
              schema:
                type: string
        '400':
          description: Validation error, malformed JSON, invalid form-data, or corrupt PDF input.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Error'
                  - $ref: '#/components/schemas/CodedError'
                  - $ref: '#/components/schemas/ValidationErrorBody'
                  - $ref: '#/components/schemas/LegacyStringError'
              examples:
                validation_error:
                  value:
                    creditsUsed: 0
                    error:
                      message: One or more validation errors occurred.
                      details:
                        - msg: Invalid value
                          param: output_type
                          location: query
                invalid_json:
                  value:
                    creditsUsed: 0
                    error:
                      code: INVALID_JSON
                      message: The request body contains invalid JSON.
                invalid_left_pdf_json:
                  value:
                    creditsUsed: 0
                    error:
                      message: Received invalid left pdf json.
                corrupt_pdf:
                  value:
                    creditsUsed: 0
                    error:
                      message: Could not process PDF. File may be corrupted.
        '413':
          $ref: '#/components/responses/RequestTooLarge'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '408':
          description: Request timed out.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                creditsUsed: 0
                error:
                  message: Request timed out.
        '500':
          description: Unexpected PDF processing failure.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                creditsUsed: 0
                error:
                  message: Unexpected PDF processing failure.
        '429':
          $ref: '#/components/responses/RateLimited'

  /pdf/richtext:
    post:
      operationId: documentRichTextDiff
      summary: Compare Documents (Rich Text)
      description: |
        Compute a rich-text diff between two PDFs with polygon geometry and optional visual object comparisons. Supports multipart/form-data uploads and JSON with base64-encoded data URLs.
      parameters:
        - $ref: '#/components/parameters/email'
        - name: output_type
          in: query
          required: true
          description: |
            Specifies the response format.
            - `json`: Structured rich-text payload with polygons + page metadata (Content-Type: application/json)
            - `html`: Self-contained HTML (Content-Type: text/html)
            - `html_json`: HTML/CSS parts in JSON (Content-Type: application/json)
            - `pdf`: Binary PDF export with rich-text highlights (Content-Type: application/pdf)
          schema:
            type: string
            enum: [json, html, html_json, pdf]
        - name: input_type
          in: query
          required: false
          description: |
            Specifies the request content-type.
            - `form`: multipart/form-data (default)
            - `json`: application/json
          schema:
            type: string
            enum: [json, form]
            default: form
        - name: diff_level
          in: query
          required: false
          description: "Specifies whether to diff by word or character. Default is `word`."
          schema:
            type: string
            enum: [word, character]
            default: word
        - name: page_image_scale
          in: query
          required: false
          description: |
            Rendering scale used for page image generation and geometry alignment.
            Allowed range is 0.25 to 4.
            For non-pdf outputs, explicit values above a document-safe runtime maximum return 400.
          schema:
            type: number
            minimum: 0.25
            maximum: 4
            default: 1
        - name: show_moves
          in: query
          required: false
          description: "Include move detection in the diff. Default is `true`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'true'
        - name: compare_font_family
          in: query
          required: false
          description: "Compare font family style changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: compare_font_size
          in: query
          required: false
          description: "Compare font size style changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: compare_font_color
          in: query
          required: false
          description: "Compare text color style changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: compare_regular_images
          in: query
          required: false
          description: "Compare raster image changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: compare_vector_graphics
          in: query
          required: false
          description: "Compare vector graphic changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: compare_form_xobjects
          in: query
          required: false
          description: "Compare form XObject changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: compare_shading_objects
          in: query
          required: false
          description: "Compare shading object changes. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: pdf_layout
          in: query
          required: false
          description: |
            Layout used when `output_type=pdf`.
            - `split-view` (default)
            - `alternating`
            - `left-document`
            - `right-document`
          schema:
            type: string
            enum: [split-view, alternating, left-document, right-document]
            default: split-view
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [left_pdf, right_pdf]
              properties:
                left_pdf:
                  type: string
                  format: binary
                  description: Left PDF file you want to diff. File extension must be .pdf.
                right_pdf:
                  type: string
                  format: binary
                  description: Right PDF file you want to diff. File extension must be .pdf.
          application/json:
            schema:
              type: object
              required: [left_pdf, right_pdf]
              properties:
                left_pdf:
                  type: string
                  description: Data URL of the left PDF (data:application/pdf;base64,...).
                right_pdf:
                  type: string
                  description: Data URL of the right PDF (data:application/pdf;base64,...).
            example:
              left_pdf: "data:application/pdf;base64,..."
              right_pdf: "data:application/pdf;base64,..."
      responses:
        '200':
          description: Rich-text diff computed successfully.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/RichTextJsonResponse'
                  - $ref: '#/components/schemas/HtmlJsonResponse'
              examples:
                json:
                  summary: output_type=json
                  value:
                    creditsUsed: 1
                    diff:
                      left:
                        - id: 0
                          type: remove
                          pageIndex: 0
                          text: [Left]
                          polygons:
                            - - [72, 720.64]
                              - [72, 706.03]
                              - [90.33, 706.03]
                              - [90.33, 720.64]
                      right:
                        - id: 0
                          type: insert
                          pageIndex: 0
                          text: [Right]
                          polygons:
                            - - [72, 720.64]
                              - [72, 706.03]
                              - [97.65, 706.03]
                              - [97.65, 720.64]
                      changeLog:
                        - chunkId: 0
                          contentBefore: Left
                          contentAfter: Right
                          contentType: text
                          type: replace
                          pageIndex: 0
                    left:
                      pageCount: 1
                      pageDimensions:
                        - width: 612
                          height: 792
                      rotations: [0]
                      pageImages: ["data:image/png;base64,..."]
                      pageImageScale: 1
                    right:
                      pageCount: 1
                      pageDimensions:
                        - width: 612
                          height: 792
                      rotations: [0]
                      pageImages: ["data:image/png;base64,..."]
                      pageImageScale: 1
                html_json:
                  summary: output_type=html_json
                  value:
                    creditsUsed: 1
                    html: '<div class="rich-text-layout">...</div>'
                    css: 'body { ... }'
            text/html:
              schema:
                type: string
                description: Self-contained HTML with page images and SVG overlays.
            application/pdf:
              schema:
                type: string
                format: binary
                description: PDF export with rich-text highlights.
        '400':
          description: Validation error, malformed PDF, or invalid page scale request.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Error'
                  - $ref: '#/components/schemas/CodedError'
                  - $ref: '#/components/schemas/ValidationErrorBody'
                  - $ref: '#/components/schemas/RichTextScaleError'
                  - $ref: '#/components/schemas/LegacyStringError'
              examples:
                validation_error:
                  summary: Invalid page_image_scale
                  value:
                    creditsUsed: 0
                    error:
                      message: One or more validation errors occurred.
                      details:
                        - msg: page_image_scale must be between 0.25 and 4.
                          param: page_image_scale
                          location: query
                explicit_scale_too_high:
                  summary: Explicit scale exceeds runtime safe max
                  value:
                    creditsUsed: 0
                    error:
                      message: Requested page_image_scale=4 exceeds the document-safe max (1.5).
                    details:
                      requested: 4
                      max_supported: 1.5
                      left_max_supported: 2
                      right_max_supported: 1.5
                bitmap_render_error:
                  summary: Page render failed at requested scale
                  value:
                    creditsUsed: 0
                    error:
                      message: Unable to render page images at the requested page_image_scale. Try a lower page_image_scale.
                corrupt_pdf:
                  summary: Corrupt PDF input
                  value:
                    creditsUsed: 0
                    error:
                      message: Could not process PDF. File may be corrupted.
                invalid_left_pdf_json:
                  summary: Invalid left PDF JSON payload
                  value:
                    creditsUsed: 0
                    error:
                      message: Received invalid left pdf json.
        '413':
          $ref: '#/components/responses/RequestTooLarge'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '408':
          description: Request timed out.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                creditsUsed: 0
                error:
                  message: Request timed out.
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          description: Unexpected PDF processing failure.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                creditsUsed: 0
                error:
                  message: Unexpected PDF processing failure.

  /image:
    post:
      operationId: imageDiff
      summary: Compare Images
      description: |
        Compute a visual diff between two images. Supports both multipart/form-data file uploads and JSON with base64-encoded data URLs.
      parameters:
        - $ref: '#/components/parameters/email'
        - name: input_type
          in: query
          required: true
          description: |
            Specifies the request content-type.
            - `form`: multipart/form-data
            - `json`: application/json
          schema:
            type: string
            enum: [json, form]
        - name: output_type
          in: query
          required: true
          description: |
            Specifies the type of output you receive in the response body.
            - `json`: Same PNG you see on the Diffchecker site for image diffs, but embedded in JSON as a data URL (Content-Type: application/json)
            - `png`: Same PNG you see on the Diffchecker site for image diffs (Content-Type: image/png)
          schema:
            type: string
            enum: [json, png]
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [left_image, right_image]
              properties:
                left_image:
                  type: string
                  format: binary
                  description: Left image file you want to diff. Accepted file extensions include .png, .jpg, .jpeg.
                right_image:
                  type: string
                  format: binary
                  description: Right image file you want to diff. Accepted file extensions include .png, .jpg, .jpeg.
          application/json:
            schema:
              type: object
              required: [left_image, right_image]
              properties:
                left_image:
                  type: string
                  description: Data URL of the left image you want to diff.
                right_image:
                  type: string
                  description: Data URL of the right image you want to diff.
      responses:
        '200':
          description: Diff computed successfully.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImageDiffJsonResponse'
              example:
                creditsUsed: 1
                dataUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABVAAAAS8CAYAAABqj6EYAAAABmJLR0QA..."
                diffPixels: 34651
                totalPixels: 234600
                misMatchPercentage: 14.77
                width: 460
                height: 510
                originalWidth: 452
                originalHeight: 477
                changedWidth: 460
                changedHeight: 510
            image/png:
              schema:
                type: string
                format: binary
                description: PNG image of the visual diff.
        '400':
          description: Invalid request data or image content.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImageValidationError'
              examples:
                validation_error:
                  value:
                    creditsUsed: 0
                    error:
                      message: One or more validation errors occurred.
                      details:
                        - msg: Invalid value
                          param: output_type
                          location: query
                corrupt_image:
                  value:
                    creditsUsed: 0
                    error:
                      message: Could not process image. File may be corrupted.
                invalid_json:
                  value:
                    creditsUsed: 0
                    error:
                      code: INVALID_JSON
                      message: The request body contains invalid JSON.
                invalid_files_json:
                  value:
                    creditsUsed: 0
                    error: Received invalid files.
        '413':
          $ref: '#/components/responses/RequestTooLarge'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          description: Image comparison failed due to a server-side processing error.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                unsupported_platform:
                  value:
                    creditsUsed: 0
                    error:
                      message: Image comparison is not supported on this platform.
                unexpected_failure:
                  value:
                    creditsUsed: 0
                    error:
                      message: Unexpected image processing failure.
        '429':
          $ref: '#/components/responses/RateLimited'

  /excel:
    post:
      operationId: excelDiff
      summary: Compare Excel Spreadsheets
      description: |
        Compute a diff between two spreadsheets. Supports both multipart/form-data file uploads and JSON with base64-encoded data URLs. Accepts .xlsx, .xls, and .csv files.
      parameters:
        - $ref: '#/components/parameters/email'
        - name: input_type
          in: query
          required: true
          description: |
            Specifies the request content-type.
            - `form`: multipart/form-data
            - `json`: application/json
          schema:
            type: string
            enum: [json, form]
        - name: diff_level
          in: query
          required: false
          description: "Specifies whether you want to diff values by their formulas or standard values. Default is `standard`."
          schema:
            type: string
            enum: [standard, formulas]
            default: standard
        - name: ignore_whitespace
          in: query
          required: false
          description: "Whether to ignore leading and trailing whitespace in the diff. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: ignore_case_changes
          in: query
          required: false
          description: "Whether to ignore case changes in the diff. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: hide_unchanged_rows
          in: query
          required: false
          description: "Whether to hide unchanged rows in the diff. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
        - name: hide_unchanged_columns
          in: query
          required: false
          description: "Whether to hide unchanged columns in the diff. Default is `false`."
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [left_spreadsheet, right_spreadsheet]
              properties:
                left_spreadsheet:
                  type: string
                  format: binary
                  description: Left spreadsheet file. File extension must be .xlsx, .xls, or .csv.
                right_spreadsheet:
                  type: string
                  format: binary
                  description: Right spreadsheet file. File extension must be .xlsx, .xls, or .csv.
                left_sheet_name:
                  type: string
                  description: The sheet name from the left spreadsheet to diff. If not provided, the first sheet will be used.
                right_sheet_name:
                  type: string
                  description: The sheet name from the right spreadsheet to diff. If not provided, the first sheet will be used.
                left_header_row:
                  type: integer
                  description: The row number to use as the header from the left spreadsheet. If not provided, the first row will be used.
                right_header_row:
                  type: integer
                  description: The row number to use as the header from the right spreadsheet. If not provided, the first row will be used.
          application/json:
            schema:
              type: object
              required: [left_spreadsheet, right_spreadsheet]
              properties:
                left_spreadsheet:
                  type: string
                  description: Data URL of the left spreadsheet (data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,...).
                right_spreadsheet:
                  type: string
                  description: Data URL of the right spreadsheet.
                left_sheet_name:
                  type: string
                  description: The sheet name from the left spreadsheet to diff. If not provided, the first sheet will be used.
                right_sheet_name:
                  type: string
                  description: The sheet name from the right spreadsheet to diff. If not provided, the first sheet will be used.
                left_header_row:
                  type: integer
                  description: The row number to use as the header from the left spreadsheet. If not provided, the first row will be used.
                right_header_row:
                  type: integer
                  description: The row number to use as the header from the right spreadsheet. If not provided, the first row will be used.
      responses:
        '200':
          description: Diff computed successfully.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExcelDiffResponse'
              example:
                creditsUsed: 1
                table:
                  - - - value: Bird
                        type: equal
                    - - value: Population
                        type: equal
                  - - - value: Kagu
                        type: equal
                    - - value: '1,000'
                        type: equal
                  - - - value: Dodo
                        type: removed
                    - - value: '0'
                        type: removed
                  - - - value: Kiwi
                        type: equal
                    - - value: '120,000'
                        type: removed
                      - value: '70,000'
                        type: inserted
                rows:
                  - type: equal
                    original: 1
                    new: 1
                  - type: equal
                    original: 2
                    new: 2
                  - type: removed
                    original: 3
                    new: null
                  - type: modified
                    original: 4
                    new: 3
                columns:
                  - type: equal
                    original: 1
                    new: 1
                  - type: modified
                    original: 2
                    new: 2
                stats:
                  moved: 0
                  inserted: 1
                  removed: 2
                  rows: 4
                  columns: 2
        '400':
          description: Invalid request data or spreadsheet content.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Error'
                  - $ref: '#/components/schemas/CodedError'
                  - $ref: '#/components/schemas/ValidationErrorBody'
              examples:
                validation_error:
                  value:
                    creditsUsed: 0
                    error:
                      message: One or more validation errors occurred.
                      details:
                        - msg: Invalid value
                          param: input_type
                          location: query
                corrupt_spreadsheet:
                  value:
                    creditsUsed: 0
                    error:
                      message: Could not process spreadsheet. File may be corrupted.
                invalid_json:
                  value:
                    creditsUsed: 0
                    error:
                      code: INVALID_JSON
                      message: The request body contains invalid JSON.
                missing_sheet:
                  value:
                    creditsUsed: 0
                    error:
                      message: Left sheet name MissingSheet not found in spreadsheet.
                invalid_header_row:
                  value:
                    creditsUsed: 0
                    error:
                      message: 'Invalid left header row: -1.'
        '413':
          $ref: '#/components/responses/RequestTooLarge'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '408':
          description: Request timed out.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                creditsUsed: 0
                error:
                  message: Request timed out.
        '500':
          description: Spreadsheet diff failed due to a server-side processing error.
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                creditsUsed: 0
                error:
                  message: Unexpected spreadsheet processing failure.
        '429':
          $ref: '#/components/responses/RateLimited'

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-Api-Key
      description: API key provided to paid subscribers.
    email:
      type: apiKey
      in: query
      name: email
      description: Your email address for free tier access.

  parameters:
    email:
      name: email
      in: query
      required: false
      description: Your email address. Required if not using an API key.
      schema:
        type: string
        format: email

  headers:
    XCreditsUsed:
      description: Credits charged for the request. Present on every public API response. JSON responses also include the same value in the `creditsUsed` body field for backwards compatibility.
      schema:
        type: integer
        example: 1
    RetryAfter:
      description: Seconds to wait before retrying the request.
      schema:
        type: integer
        example: 120

  schemas:
    Error:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the request. Rejected requests usually report `0`.
        error:
          type: object
          properties:
            message:
              type: string
              description: A human-readable error message.
          required:
            - message
      required: [creditsUsed, error]

    CodedError:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the request. Rejected requests usually report `0`.
        error:
          type: object
          properties:
            message:
              type: string
              description: A human-readable error message.
            code:
              type: string
              description: A machine-readable error code.
          required: [message, code]
      required: [creditsUsed, error]

    RetriableCodedError:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the request. Rejected requests usually report `0`.
        retryAfterSeconds:
          type: integer
          description: Seconds until the request should be retried.
        error:
          type: object
          properties:
            message:
              type: string
              description: A human-readable error message.
            code:
              type: string
              description: A machine-readable error code.
          required: [message, code]
      required: [creditsUsed, retryAfterSeconds, error]

    ValidationErrorBody:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the request. Rejected requests usually report `0`.
        error:
          type: object
          properties:
            message:
              type: string
              description: A human-readable error message.
            details:
              type: array
              description: Additional validation details.
              items:
                type: object
          required: [message, details]
      required: [creditsUsed, error]

    LegacyStringError:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the request. Rejected requests usually report `0`.
        error:
          type: string
          description: A legacy validation error message.
      required: [creditsUsed, error]

    ImageValidationError:
      description: Image validation errors can be returned either as an object or a legacy string.
      oneOf:
        - $ref: '#/components/schemas/Error'
        - $ref: '#/components/schemas/CodedError'
        - $ref: '#/components/schemas/ValidationErrorBody'
        - $ref: '#/components/schemas/LegacyStringError'

    TextDiffJsonResponse:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the diff request.
        rows:
          type: array
          items:
            type: object
            properties:
              end:
                type: boolean
              start:
                type: boolean
              insideChanged:
                type: boolean
              left:
                $ref: '#/components/schemas/DiffSide'
              right:
                $ref: '#/components/schemas/DiffSide'
        added:
          type: integer
          description: Number of added chunks.
        removed:
          type: integer
          description: Number of removed chunks.
      required: [creditsUsed, rows, added, removed]

    DiffSide:
      type: object
      properties:
        chunks:
          type: array
          items:
            type: object
            properties:
              value:
                type: string
              type:
                type: string
                enum: [equal, insert, remove]
        line:
          type: integer

    HtmlJsonResponse:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the diff request.
        html:
          type: string
          description: HTML markup of the diff table.
        css:
          type: string
          description: CSS styles for the diff table.
      required: [creditsUsed, html, css]

    RichTextPoint:
      type: array
      description: A single polygon point in PDF coordinate space as [x, y].
      items:
        type: number

    RichTextPolygon:
      type: array
      description: Polygon outline made of RichTextPoint items.
      items:
        $ref: '#/components/schemas/RichTextPoint'

    RichTextMoveSegments:
      type: object
      properties:
        type:
          type: string
          enum: [remove, insert]
        polygons:
          type: array
          items:
            $ref: '#/components/schemas/RichTextPolygon'

    RichTextChunk:
      type: object
      description: |
        Represents a single diff chunk. Text chunks include `text`.
        Visual chunks can include `image` metadata. Equal text chunks can expose style flags.
      properties:
        id:
          type: integer
        type:
          type: string
          enum: [equal, insert, remove, move]
        pageIndex:
          type: integer
        text:
          type: array
          items:
            type: string
        polygons:
          type: array
          items:
            $ref: '#/components/schemas/RichTextPolygon'
        moveSegments:
          $ref: '#/components/schemas/RichTextMoveSegments'
        fontFamilyChanged:
          type: boolean
        fontSizeChanged:
          type: boolean
        colorChanged:
          type: boolean
        image:
          type: object
          properties:
            hash:
              type: string
            objectType:
              type: string
              enum: [image, vector, form, shading]
            imageUrl:
              type: string
              description: Base64-encoded image data URL.
            width:
              type: number
            height:
              type: number
            byteSize:
              type: number
            bounds:
              type: object
              properties:
                left:
                  type: number
                top:
                  type: number
                right:
                  type: number
                bottom:
                  type: number
      required: [id, type, pageIndex, polygons]

    RichTextStyleChange:
      type: object
      properties:
        chunkId:
          type: integer
        text:
          type: string
        type:
          type: string
          enum: [style]
        chunkBefore:
          $ref: '#/components/schemas/RichTextChunk'
        chunkAfter:
          $ref: '#/components/schemas/RichTextChunk'

    RichTextChangeLogItem:
      type: object
      properties:
        chunkId:
          type: integer
        contentBefore:
          type: string
        contentAfter:
          type: string
        contentType:
          type: string
          enum: [text, visual]
        type:
          type: string
          enum: [insert, remove, replace, move, style]
        pageIndex:
          type: integer

    RichTextPageDimensions:
      type: object
      properties:
        width:
          type: number
        height:
          type: number
      required: [width, height]

    RichTextPageInfo:
      type: object
      properties:
        pageCount:
          type: integer
        pageDimensions:
          type: array
          items:
            $ref: '#/components/schemas/RichTextPageDimensions'
        rotations:
          type: array
          items:
            type: integer
        pageImages:
          type: array
          items:
            type: string
            description: Page image data URL.
        pageImageScale:
          type: number
      required: [pageCount, pageDimensions, rotations, pageImageScale]

    RichTextDiffPayload:
      type: object
      properties:
        left:
          type: array
          items:
            $ref: '#/components/schemas/RichTextChunk'
        right:
          type: array
          items:
            $ref: '#/components/schemas/RichTextChunk'
        changeLog:
          type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/RichTextChangeLogItem'
              - $ref: '#/components/schemas/RichTextStyleChange'
      required: [left, right, changeLog]

    RichTextJsonResponse:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the diff request.
        diff:
          $ref: '#/components/schemas/RichTextDiffPayload'
        left:
          $ref: '#/components/schemas/RichTextPageInfo'
        right:
          $ref: '#/components/schemas/RichTextPageInfo'
      required: [creditsUsed, diff, left, right]

    RichTextScaleErrorDetails:
      type: object
      properties:
        requested:
          type: number
        max_supported:
          type: number
        left_max_supported:
          type: number
        right_max_supported:
          type: number
      required: [requested, max_supported, left_max_supported, right_max_supported]

    RichTextScaleError:
      type: object
      properties:
        creditsUsed:
          type: integer
        error:
          type: object
          properties:
            message:
              type: string
          required: [message]
        details:
          $ref: '#/components/schemas/RichTextScaleErrorDetails'
      required: [creditsUsed, error, details]

    ImageDiffJsonResponse:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the diff request.
        dataUrl:
          type: string
          description: Data URL of the diff image (PNG).
        diffPixels:
          type: integer
          description: Number of pixels that differ.
        totalPixels:
          type: integer
          description: Total number of pixels compared.
        misMatchPercentage:
          type: number
          format: float
          description: Percentage of pixels that differ.
        width:
          type: integer
        height:
          type: integer
        originalWidth:
          type: integer
        originalHeight:
          type: integer
        changedWidth:
          type: integer
        changedHeight:
          type: integer
      required:
        [
          creditsUsed,
          dataUrl,
          diffPixels,
          totalPixels,
          misMatchPercentage,
          width,
          height,
          originalWidth,
          originalHeight,
          changedWidth,
          changedHeight,
        ]

    ExcelDiffResponse:
      type: object
      properties:
        creditsUsed:
          type: integer
          description: Credits charged for the diff request.
        table:
          type: array
          description: |
            The diff results for each cell. Cells are organized by rows, with each row containing cells in column order (left to right). Each cell is represented by an array of one or two objects. If the cell value changed, there will be two objects (one removed, one inserted). Each object contains the `type` of change and the `value`.
          items:
            type: array
            items:
              type: array
              items:
                type: object
                properties:
                  value:
                    type: string
                  type:
                    type: string
                    enum: [equal, removed, inserted]
        rows:
          type: array
          description: |
            Row-level diff metadata. Each entry contains the original row number, the new row number, and the type of change.
          items:
            type: object
            properties:
              type:
                type: string
                enum: [equal, removed, inserted, modified]
              original:
                type: integer
                nullable: true
              new:
                type: integer
                nullable: true
        columns:
          type: array
          description: |
            Column-level diff metadata. Each entry contains the original column number, the new column number, and the type of change.
          items:
            type: object
            properties:
              type:
                type: string
                enum: [equal, removed, inserted, modified]
              original:
                type: integer
                nullable: true
              new:
                type: integer
                nullable: true
        stats:
          type: object
          description: Summary statistics for the diff.
          properties:
            moved:
              type: integer
            inserted:
              type: integer
            removed:
              type: integer
            rows:
              type: integer
              description: Total rows in the output.
            columns:
              type: integer
              description: Total columns in the output.
      required: [creditsUsed, table, rows, columns, stats]

  responses:
    ValidationError:
      description: One or more validation errors occurred, or the JSON body could not be parsed.
      headers:
        X-Credits-Used:
          $ref: '#/components/headers/XCreditsUsed'
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ValidationErrorBody'
              - $ref: '#/components/schemas/CodedError'
          examples:
            validation_error:
              value:
                creditsUsed: 0
                error:
                  message: One or more validation errors occurred.
                  details:
                    - msg: Invalid value
                      param: output_type
                      location: query
            invalid_json:
              value:
                creditsUsed: 0
                error:
                  code: INVALID_JSON
                  message: The request body contains invalid JSON.
    Unauthorized:
      description: Authentication failed. Provide a valid email or API key. Depending on the failure, the response can include more specific error codes.
      headers:
        X-Credits-Used:
          $ref: '#/components/headers/XCreditsUsed'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CodedError'
          examples:
            unauthorized:
              value:
                creditsUsed: 0
                error:
                  code: UNAUTHORIZED
                  message: In order to use the API, please provide an API key via the "X-Api-Key" header or email via the "email" key in the query string.
            invalid_email:
              value:
                creditsUsed: 0
                error:
                  code: INVALID_EMAIL
                  message: In order to use the free API, please provide a valid email via the "email" key in the query string.
            no_default_email:
              value:
                creditsUsed: 0
                error:
                  code: NO_DEFAULT_EMAIL
                  message: Please change the email in your request from test@test.com to your own email.
            invalid_api_key:
              value:
                creditsUsed: 0
                error:
                  code: API_KEY_INVALID
                  message: Please provide a valid API key.
            subscription_expired:
              value:
                creditsUsed: 0
                error:
                  code: SUBSCRIPTION_EXPIRED
                  message: Subscription has expired. Please renew your subscription or upgrade to a higher plan.
    RateLimited:
      description: Rate limit exceeded. Depending on the throttle, the response may also include `retryAfterSeconds` and a `Retry-After` response header.
      headers:
        X-Credits-Used:
          $ref: '#/components/headers/XCreditsUsed'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/CodedError'
              - $ref: '#/components/schemas/RetriableCodedError'
          examples:
            free_diff_limit:
              value:
                creditsUsed: 0
                error:
                  code: FREE_DIFF_LIMIT_EXCEEDED
                  message: Free request limit exceeded - please wait until the end of the month or upgrade to a paid plan.
            paid_diff_limit:
              value:
                creditsUsed: 0
                error:
                  code: PAID_DIFF_LIMIT_EXCEEDED
                  message: Credit limit exceeded - please wait until the end of the billing period or upgrade your plan.
            failed_request_limit:
              value:
                creditsUsed: 0
                retryAfterSeconds: 120
                error:
                  code: FAILED_REQUEST_LIMIT_EXCEEDED
                  message: Too many failed API requests that used 0 credits. Please wait a few minutes and try again.
            failed_request_daily_limit:
              value:
                creditsUsed: 0
                retryAfterSeconds: 3600
                error:
                  code: FAILED_REQUEST_DAILY_LIMIT_EXCEEDED
                  message: Too many failed API requests that used 0 credits in the last 24 hours. Please try again later.
    RequestTooLarge:
      description: The request exceeded the active size limit for the caller's authentication method or plan.
      headers:
        X-Credits-Used:
          $ref: '#/components/headers/XCreditsUsed'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CodedError'
          example:
            creditsUsed: 0
            error:
              code: LIMIT_FILE_SIZE
              message: Request too large! Max allowed size is 5 MB
