openapi: 3.1.0
info:
  title: Convert Guru x402 File Conversion API
  version: 1.0.0
  description: >-
    Machine-payable file type detection and file conversion API for AI agents.
    Detect the real type of a file from metadata and the first 200 bytes, start
    an asynchronous file conversion job, poll its status, and purchase the
    completed result only if conversion succeeds. Conversion result pricing is
    predictable: 0.02 USDC for files up to 10 MB, and 0.04 USDC for files over
    10 MB up to the maximum file size of 40 MB.
  x-guidance: >-
    Use POST /api/x402/v1/detect-file-type to detect the likely real file type.
    Use POST /api/x402/v1/convert-file to start an asynchronous conversion job.
    POST /convert-file requires the uploaded file, target_format, filename,
    created_at, and size_bytes. It returns a job_id and status_url; it does not
    return the converted file or result_url immediately. Poll GET
    /api/x402/v1/convert-file/{job_id}/status until status is done or failed. If
    status is done, the status response includes result_url. Call result_url and
    pay the x402 exact result price. Result pricing is tiered and predictable:
    0.02 USDC for files up to 10 MB, 0.04 USDC for files over 10 MB and up to 40
    MB. After successful payment, the result endpoint returns a short-lived
    download_url.
x-service-info:
  categories:
    - media
    - developer-tools
    - compute
  docs:
    homepage: https://convert.guru/api/x402/v1/
    llms: https://convert.guru/llms.txt
    apiReference: https://convert.guru/openapi.json
servers:
  - url: https://convert.guru
    description: Production (Mainnet networks: eip155:8453, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)
tags:
  - name: Landing
    description: Human- and agent-readable landing page for the x402 API.
  - name: Discovery
    description: Machine-readable discovery and health endpoints.
  - name: File Detection
    description: Detect the likely real format of a file.
  - name: File Conversion
    description: Start, monitor, and purchase file conversion results.
paths:
  /api/x402/v1/:
    get:
      tags:
        - Landing
      operationId: getX402ApiLandingPage
      summary: Get x402 API landing page
      description: >-
        Returns a one-page HTML landing page for humans and AI agents. This page
        should link to /openapi.json, /openapi.yaml, /llms.txt, and
        /.well-known/x402.json.
      responses:
        '200':
          description: HTML landing page for the x402 API.
          content:
            text/html:
              schema:
                type: string
              example: >-
                <!doctype html><html><head><title>Convert Guru x402
                API</title></head><body><h1>Convert Guru x402
                API</h1></body></html>
  /openapi.json:
    get:
      tags:
        - Discovery
      operationId: getOpenApiJson
      summary: Get OpenAPI document as JSON
      description: Returns the OpenAPI 3.1 document in JSON format.
      responses:
        '200':
          description: OpenAPI JSON document.
          content:
            application/json:
              schema:
                type: object
  /openapi.yaml:
    get:
      tags:
        - Discovery
      operationId: getOpenApiYaml
      summary: Get OpenAPI document as YAML
      description: Returns the OpenAPI 3.1 document in YAML format.
      responses:
        '200':
          description: OpenAPI YAML document.
          content:
            application/yaml:
              schema:
                type: string
            text/yaml:
              schema:
                type: string
  /api/x402/v1/health:
    get:
      tags:
        - Discovery
      operationId: getHealth
      summary: Health check
      description: Returns service health for monitoring and agent preflight checks.
      responses:
        '200':
          description: Service is healthy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
              examples:
                ok:
                  value:
                    status: ok
                    service: convert-guru-x402
                    version: 1.0.0
  /api/x402/v1/detect-file-type:
    post:
      tags:
        - File Detection
      operationId: detectFileType
      summary: Detect the real file type
      description: >-
        Detects the likely real file format from filename, file creation time,
        file size, and the first 200 bytes of the file encoded as base64.
      security:
        - PaymentSignatureHeader: []
        - XPaymentHeader: []
      x-payment-info:
        price:
          mode: fixed
          currency: USDC
          amount: '0.002000'
        protocols:
          - x402:
              scheme: exact
              networks:
                - eip155:8453
                - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
        offers:
          - protocol: x402
            scheme: exact
            price:
              amount: '0.002000'
              currency: USDC
            networks:
              - eip155:8453
              - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DetectFileTypeRequest'
            examples:
              generic001ActuallyPdf:
                summary: .001 file that is probably a PDF
                value:
                  filename: document.001
                  created_at: '2026-05-21T14:30:00Z'
                  size_bytes: 184221
                  header_first_200_bytes_base64: JVBERi0xLjQKJcTl8uXrp...
      responses:
        '200':
          description: File type detection result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DetectFileTypeResponse'
              examples:
                pdfDetected:
                  value:
                    detected: true
                    input_filename: document.001
                    suggested_filename: document.pdf
                    detected_format:
                      format: pdf
                      description: PDF document
                      confidence: 0.99
                      confidence_percent: 99
                      canonical_extension: pdf
                    convertible_to:
                      - docx
                      - txt
                      - png
                      - jpg
                      - html
                    convert_file_compatibility:
                      status: likely
                      confidence: 0.8
                      reason: >-
                        The header matches PDF, but only the first 200 bytes
                        were inspected.
                unknown:
                  value:
                    detected: false
                    input_filename: unknown.bin
                    suggested_filename: null
                    detected_format: null
                    convertible_to: []
                    convert_file_compatibility:
                      status: unknown
                      confidence: 0
                      reason: >-
                        The first 200 bytes were not enough to identify the file
                        type reliably.
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/x402/v1/convert-file:
    post:
      tags:
        - File Conversion
      operationId: createFileConversionJob
      summary: Start a file conversion job
      description: >-
        Uploads a file and starts an asynchronous conversion job. This endpoint
        returns a job_id and status_url. It does not return the converted file
        or result_url immediately. The request must include filename,
        created_at, and size_bytes in addition to the file so the service can
        improve source-format detection even when the extension is wrong,
        missing, or unknown. Maximum file size is 40 MB. A small non-refundable
        job creation fee may be charged to reserve processing capacity and
        reduce spam. The final conversion result is charged separately only if
        the conversion succeeds. Final result pricing is predictable: 0.02 USDC
        for files up to 10 MB, and 0.04 USDC for files over 10 MB and up to 40
        MB.
      security:
        - PaymentSignatureHeader: []
        - XPaymentHeader: []
      x-payment-info:
        price:
          mode: fixed
          currency: USDC
          amount: '0.002000'
        protocols:
          - x402:
              scheme: exact
              networks:
                - eip155:8453
                - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
        offers:
          - protocol: x402
            scheme: exact
            price:
              amount: '0.002000'
              currency: USDC
            networks:
              - eip155:8453
              - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        description: >-
          Send as multipart/form-data. The schema property names are form-data
          part names, not JSON keys. The `file` part contains the raw file
          bytes.
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/CreateFileConversionJobRequest'
            encoding:
              file:
                contentType: application/octet-stream
            examples:
              fileToDocx:
                summary: Convert unknown input to DOCX
                value:
                  file: <binary file bytes>
                  target_format: docx
                  filename: document.001
                  created_at: '2026-05-21T14:30:00Z'
                  size_bytes: 184221
      responses:
        '202':
          description: Conversion job accepted and processing.
          headers:
            Retry-After:
              description: Suggested seconds before polling status_url.
              schema:
                type: integer
                example: 5
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversionJobAccepted'
              examples:
                accepted:
                  value:
                    job_id: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
                    status: processing
                    status_url: >-
                      https://convert.guru/api/x402/v1/convert-file/job_7QmN9vK2xYp4TzRb6HcD8sWfA3/status
                    retry_after_seconds: 5
                    message: >-
                      Conversion started. Poll status_url until status is done
                      or failed.
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '413':
          $ref: '#/components/responses/FileTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/x402/v1/convert-file/{job_id}/status:
    get:
      tags:
        - File Conversion
      operationId: getFileConversionStatus
      summary: Get file conversion status
      description: >-
        Checks the status of a conversion job. This endpoint is free and uses
        the job_id returned by POST /convert-file as the access key. If the job
        is done, the response includes the final result price and result_url to
        purchase the converted file. Result pricing is tiered by uploaded file
        size: 0.02 USDC up to 10 MB, 0.04 USDC over 10 MB up to 40 MB.
      parameters:
        - $ref: '#/components/parameters/JobId'
      responses:
        '200':
          description: Current conversion job status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversionJobStatus'
              examples:
                processing:
                  value:
                    job_id: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
                    status: processing
                    retry_after_seconds: 5
                    result_available: false
                    message: Conversion is still processing.
                doneSmallFile:
                  value:
                    job_id: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
                    status: done
                    detected_input_format: pdf
                    target_format: docx
                    input_file_size_bytes: 184221
                    result_available: true
                    result_requires_payment: true
                    price_for_result:
                      amount: '0.020000'
                      currency: USDC
                      atomic_units: '20000'
                      decimals: 6
                      network: eip155:8453
                      pricing_rule: file_size_lte_10mb
                    result_url: >-
                      https://convert.guru/api/x402/v1/convert-file/job_7QmN9vK2xYp4TzRb6HcD8sWfA3/result
                    price_expires_at: '2026-05-21T18:00:00Z'
                doneLargeFile:
                  value:
                    job_id: job_Av9P2sQ6Lm4Tn8Rx3Bz5Yk7Wc1
                    status: done
                    detected_input_format: avi
                    target_format: mpeg
                    input_file_size_bytes: 22020096
                    result_available: true
                    result_requires_payment: true
                    price_for_result:
                      amount: '0.040000'
                      currency: USDC
                      atomic_units: '40000'
                      decimals: 6
                      network: eip155:8453
                      pricing_rule: file_size_gt_10mb_lte_40mb
                    result_url: >-
                      https://convert.guru/api/x402/v1/convert-file/job_Av9P2sQ6Lm4Tn8Rx3Bz5Yk7Wc1/result
                    price_expires_at: '2026-05-21T18:00:00Z'
                failed:
                  value:
                    job_id: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
                    status: failed
                    failure_type: unsupported_or_corrupt_input
                    result_available: false
                    charged_amount:
                      amount: '0.000000'
                      currency: USDC
                      atomic_units: '0'
                      decimals: 6
                    message: The uploaded file could not be converted.
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/JobNotFound'
        '410':
          $ref: '#/components/responses/JobExpired'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/x402/v1/convert-file/{job_id}/result:
    get:
      tags:
        - File Conversion
      operationId: purchaseFileConversionResult
      summary: Purchase completed converted file result
      description: >-
        Purchases access to the completed conversion result using x402 exact
        payment. Result price is 0.02 USDC up to 10 MB and 0.04 USDC over 10 MB
        up to 40 MB.
      security:
        - PaymentSignatureHeader: []
        - XPaymentHeader: []
      x-payment-info:
        price:
          mode: dynamic
          currency: USDC
          min: '0.020000'
          max: '0.040000'
        protocols:
          - x402:
              scheme: exact
              networks:
                - eip155:8453
                - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
        offers:
          - protocol: x402
            scheme: exact
            price:
              mode: dynamic
              min: '0.020000'
              max: '0.040000'
              currency: USDC
            networks:
              - eip155:8453
              - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
      parameters:
        - $ref: '#/components/parameters/JobId'
      responses:
        '200':
          description: Payment accepted. Short-lived download URL returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PurchasedConversionResult'
              examples:
                paid:
                  value:
                    job_id: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
                    status: paid
                    download_url: >-
                      https://convert.guru/tmp/dl_4f9c8b7e2a1d4c6fa9910de832bb19aa.docx
                    filename: converted.docx
                    mime_type: >-
                      application/vnd.openxmlformats-officedocument.wordprocessingml.document
                    result_size_bytes: 184221
                    payment:
                      protocol: x402
                      scheme: exact
                      network: eip155:8453
                      amount:
                        amount: '0.020000'
                        currency: USDC
                        atomic_units: '20000'
                        decimals: 6
                      tx_hash: 0x...
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/JobNotFound'
        '409':
          description: Conversion result is not ready or job failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notReady:
                  value:
                    error:
                      code: result_not_ready
                      message: >-
                        Conversion is still processing. Poll the status_url
                        first.
                failed:
                  value:
                    error:
                      code: conversion_failed
                      message: Conversion failed and no payable result is available.
        '410':
          $ref: '#/components/responses/JobExpired'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/x402/v1/convert-file/{job_id}/delete:
    delete:
      tags:
        - File Conversion
      operationId: deleteFileConversionResult
      summary: Delete converted file and job details immediately
      description: >-
        Deletes the converted file and its associated job details immediately.
        Use this to ensure the file is permanently deleted after a successful
        download, instead of waiting for the automatic 24-hour cleanup.
      parameters:
        - $ref: '#/components/parameters/JobId'
      responses:
        '200':
          description: File and job details deleted successfully.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - message
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Converted file and job details deleted successfully.
        '404':
          $ref: '#/components/responses/JobNotFound'
        '410':
          $ref: '#/components/responses/JobExpired'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  securitySchemes:
    PaymentSignatureHeader:
      type: apiKey
      in: header
      name: PAYMENT-SIGNATURE
      description: x402 v2 payment signature header sent after receiving HTTP 402.
    XPaymentHeader:
      type: apiKey
      in: header
      name: X-PAYMENT
      description: Legacy x402 payment payload header (supported for backwards compatibility).
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >-
        Optional client-generated key to avoid duplicate conversion jobs and
        duplicate job creation fees when retrying POST /convert-file.
      schema:
        type: string
        minLength: 8
        maxLength: 128
      example: idem_01HZX9Y5W7K3
    JobId:
      name: job_id
      in: path
      required: true
      description: >-
        Conversion job ID returned by POST /convert-file. Treat it as a bearer
        secret until the job expires.
      schema:
        type: string
        pattern: ^job_[A-Za-z0-9_-]{22,80}$
      example: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
  schemas:
    HealthResponse:
      type: object
      required:
        - status
        - service
        - version
      properties:
        status:
          type: string
          enum:
            - ok
        service:
          type: string
          example: convert-guru-x402
        version:
          type: string
          example: 1.0.0
    DetectFileTypeRequest:
      type: object
      required:
        - filename
        - size_bytes
        - header_first_200_bytes_base64
      properties:
        filename:
          type: string
          maxLength: 255
          description: Original filename.
          example: document.001
        created_at:
          type: string
          format: date-time
          description: File creation time in ISO 8601 / RFC 3339 format if available.
          example: '2026-05-21T14:30:00Z'
        size_bytes:
          type: integer
          minimum: 0
          description: Total file size in bytes.
          example: 184221
        header_first_200_bytes_base64:
          type: string
          contentEncoding: base64
          description: >-
            Base64-encoded first 200 bytes of the file. If the file is smaller
            than 200 bytes, encode all available bytes.
          example: JVBERi0xLjQKJcTl8uXrp...
    DetectFileTypeResponse:
      type: object
      required:
        - detected
        - input_filename
        - suggested_filename
        - detected_format
        - convertible_to
        - convert_file_compatibility
      properties:
        detected:
          type: boolean
          description: Whether the service could identify a likely file type.
          example: true
        input_filename:
          type: string
          description: The filename provided in the request.
          example: document.001
        suggested_filename:
          oneOf:
            - type: string
            - type: 'null'
          description: >-
            Suggested filename using the canonical detected extension if
            available.
          example: document.pdf
        detected_format:
          oneOf:
            - $ref: '#/components/schemas/DetectedFormat'
            - type: 'null'
        convertible_to:
          type: array
          description: >-
            Flat list of target formats into which the detected file type can
            likely be converted.
          items:
            type: string
          example:
            - docx
            - txt
            - png
            - jpg
            - html
        convert_file_compatibility:
          $ref: '#/components/schemas/ConvertFileCompatibility'
    DetectedFormat:
      type: object
      required:
        - format
        - description
        - confidence
        - confidence_percent
        - canonical_extension
      properties:
        format:
          type: string
          description: Lowercase canonical format identifier used by the API.
          example: pdf
        description:
          type: string
          example: PDF document
        confidence:
          type: number
          minimum: 0
          maximum: 1
          example: 0.99
        confidence_percent:
          type: integer
          minimum: 0
          maximum: 100
          example: 99
        canonical_extension:
          type: string
          description: Normal file extension for the detected type, without dot.
          example: pdf
    ConvertFileCompatibility:
      type: object
      required:
        - status
        - confidence
        - reason
      properties:
        status:
          type: string
          enum:
            - 'yes'
            - likely
            - maybe
            - 'no'
            - unknown
          description: Estimated compatibility with POST /api/x402/v1/convert-file.
          example: likely
        confidence:
          type: number
          minimum: 0
          maximum: 1
          example: 0.8
        reason:
          type: string
          example: The header matches PDF, but only the first 200 bytes were inspected.
    CreateFileConversionJobRequest:
      type: object
      required:
        - file
        - target_format
        - filename
        - created_at
        - size_bytes
      properties:
        file:
          type: string
          format: binary
          description: File to convert. The extension may be missing, unknown, or wrong.
        target_format:
          type: string
          minLength: 1
          maxLength: 20
          description: Desired output format without dot, e.g. pdf, docx, jpg, webp, mp4.
          examples:
            - docx
            - webp
            - pdf
        filename:
          type: string
          maxLength: 255
          description: >-
            Original filename, including extension if available. Used as a
            source-format hint.
          example: document.001
        created_at:
          type: string
          format: date-time
          description: >-
            File creation time in ISO 8601 / RFC 3339 format. Used as a
            detection hint.
          example: '2026-05-21T14:30:00Z'
        size_bytes:
          type: integer
          minimum: 1
          maximum: 41943040
          description: >-
            Total uploaded file size in bytes. Maximum allowed size is 40 MB,
            i.e. 41,943,040 bytes.
          example: 184221
    ConversionJobAccepted:
      type: object
      required:
        - job_id
        - status
        - status_url
        - retry_after_seconds
      properties:
        job_id:
          type: string
          example: job_7QmN9vK2xYp4TzRb6HcD8sWfA3
        status:
          type: string
          enum:
            - processing
        status_url:
          type: string
          format: uri
        retry_after_seconds:
          type: integer
          minimum: 1
          example: 5
        message:
          type: string
    ConversionJobStatus:
      oneOf:
        - $ref: '#/components/schemas/ConversionJobProcessing'
        - $ref: '#/components/schemas/ConversionJobDone'
        - $ref: '#/components/schemas/ConversionJobFailed'
    ConversionJobProcessing:
      type: object
      required:
        - job_id
        - status
        - result_available
        - retry_after_seconds
      properties:
        job_id:
          type: string
        status:
          type: string
          enum:
            - queued
            - processing
        result_available:
          type: boolean
          const: false
        retry_after_seconds:
          type: integer
          minimum: 1
          example: 5
        progress_percent:
          type: integer
          minimum: 0
          maximum: 99
          example: 42
        message:
          type: string
    ConversionJobDone:
      type: object
      required:
        - job_id
        - status
        - input_file_size_bytes
        - result_available
        - result_requires_payment
        - price_for_result
        - result_url
      properties:
        job_id:
          type: string
        status:
          type: string
          enum:
            - done
        detected_input_format:
          type: string
          example: pdf
        target_format:
          type: string
          example: docx
        input_file_size_bytes:
          type: integer
          minimum: 1
          maximum: 41943040
          description: >-
            Original input file size in bytes, used to determine the result
            price.
          example: 184221
        result_available:
          type: boolean
          const: true
        result_requires_payment:
          type: boolean
          const: true
        price_for_result:
          $ref: '#/components/schemas/ResultPrice'
        result_url:
          type: string
          format: uri
        price_expires_at:
          type: string
          format: date-time
        result_expires_at:
          type: string
          format: date-time
    ConversionJobFailed:
      type: object
      required:
        - job_id
        - status
        - failure_type
        - result_available
      properties:
        job_id:
          type: string
        status:
          type: string
          enum:
            - failed
        failure_type:
          type: string
          enum:
            - unsupported_or_corrupt_input
            - unsupported_output_format
            - file_too_large
            - security_rejected
            - provider_error
            - timeout
            - unknown
        result_available:
          type: boolean
          const: false
        charged_amount:
          $ref: '#/components/schemas/Price'
        message:
          type: string
    PurchasedConversionResult:
      type: object
      required:
        - job_id
        - status
        - download_url
        - filename
        - mime_type
        - result_size_bytes
      properties:
        job_id:
          type: string
        status:
          type: string
          enum:
            - paid
        download_url:
          type: string
          format: uri
          description: >-
            Short-lived URL (valid for 1 hour) for downloading the converted file without paying
            again. The URL path contains an unguessable random filename. By
            default, the file is deleted after 24h. If the file should be
            deleted immediately after a successful download, call
            /api/x402/v1/convert-file/{job_id}/delete.
        filename:
          type: string
          example: converted.docx
        mime_type:
          type: string
          example: >-
            application/vnd.openxmlformats-officedocument.wordprocessingml.document
        result_size_bytes:
          type: integer
          minimum: 0
          description: Converted result file size in bytes.
          example: 184221
        payment:
          $ref: '#/components/schemas/PaymentReceipt'
    ResultPrice:
      type: object
      required:
        - amount
        - currency
        - atomic_units
        - decimals
        - pricing_rule
      properties:
        amount:
          type: string
          pattern: ^[0-9]+(\.[0-9]{1,6})?$
          example: '0.020000'
        currency:
          type: string
          enum:
            - USDC
          example: USDC
        atomic_units:
          type: string
          pattern: ^[0-9]+$
          example: '20000'
        decimals:
          type: integer
          enum:
            - 6
          example: 6
        network:
          type: string
          example: eip155:8453
        pricing_rule:
          type: string
          examples:
            - file_size_lte_10mb
            - file_size_gt_10mb_lte_40mb
    Price:
      type: object
      required:
        - amount
        - currency
        - atomic_units
        - decimals
      properties:
        amount:
          type: string
          pattern: ^[0-9]+(\.[0-9]{1,6})?$
          example: '0.020000'
        currency:
          type: string
          enum:
            - USDC
          example: USDC
        atomic_units:
          type: string
          pattern: ^[0-9]+$
          example: '20000'
        decimals:
          type: integer
          enum:
            - 6
          example: 6
        network:
          type: string
          example: eip155:8453
    PaymentReceipt:
      type: object
      required:
        - protocol
        - scheme
        - network
        - amount
      properties:
        protocol:
          type: string
          enum:
            - x402
        scheme:
          type: string
          enum:
            - exact
        network:
          type: string
          example: eip155:8453
        amount:
          $ref: '#/components/schemas/Price'
        tx_hash:
          type: string
          example: 0x...
        settled_at:
          type: string
          format: date-time
    X402PaymentRequired:
      type: object
      description: x402 payment challenge. Exact shape may depend on x402 library version.
      required:
        - x402Version
        - accepts
      properties:
        x402Version:
          type: integer
          example: 2
        accepts:
          type: array
          items:
            type: object
            required:
              - scheme
              - network
              - amount
              - asset
              - payTo
              - resource
            properties:
              scheme:
                type: string
                enum:
                  - exact
              network:
                type: string
                example: eip155:8453
              amount:
                type: string
                description: Amount in atomic units.
                example: '20000'
              asset:
                type: string
                description: Token contract address or token identifier.
              payTo:
                type: string
                description: Merchant wallet address.
              resource:
                type: string
                format: uri
              description:
                type: string
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: validation_error
            message:
              type: string
              example: Invalid target_format.
            details:
              type: object
              additionalProperties: true
    UploadedFileMetadata:
      type: object
      required:
        - original_filename
        - created_at
        - size_bytes
      properties:
        original_filename:
          type: string
          example: document.001
        created_at:
          type: string
          format: date-time
          example: '2026-05-21T14:30:00Z'
        size_bytes:
          type: integer
          minimum: 1
          maximum: 41943040
          example: 184221
    PricingTier:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          enum:
            - standard
            - large
          description: >-
            standard = up to and including 10 MB; large = over 10 MB and up to
            40 MB.
        max_size_bytes:
          type: integer
          example: 10485760
        min_size_bytes:
          type: integer
          example: 10485761
    ResultPricing:
      type: object
      required:
        - mode
        - currency
        - settlement_asset
        - tiers
        - max_file_size_bytes
      properties:
        mode:
          type: string
          enum:
            - tiered_by_uploaded_file_size
        currency:
          type: string
          enum:
            - USDC
        settlement_asset:
          type: string
          enum:
            - USDC
        tiers:
          type: array
          items:
            type: object
            properties:
              min_size_bytes:
                type: integer
              max_size_bytes:
                type: integer
              amount:
                type: string
              atomic_units:
                type: string
          example:
            - max_size_bytes: 10485760
              amount: '0.020000'
              atomic_units: '20000'
            - min_size_bytes: 10485761
              max_size_bytes: 41943040
              amount: '0.040000'
              atomic_units: '40000'
        max_file_size_bytes:
          type: integer
          example: 41943040
  responses:
    PaymentRequired:
      description: x402 payment required.
      headers:
        PAYMENT-REQUIRED:
          description: Base64-encoded payment requirements challenge (EIP-3009/x402 requirements).
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/X402PaymentRequired'
    BadRequest:
      description: Bad request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ValidationError:
      description: Request validation failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    JobNotFound:
      description: Job not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    JobExpired:
      description: Job or result expired.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    FileTooLarge:
      description: Uploaded file exceeds the maximum allowed size.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    UnsupportedMediaType:
      description: Uploaded media type is not accepted.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: Too many requests.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds before retrying.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    IdempotencyConflict:
      description: Idempotency key was reused with different request parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
x-pricing-policy:
  currency: USDC
  decimals: 6
  detect_file_type_fee:
    amount: '0.002000'
    atomic_units: '2000'
    applies_to: POST /api/x402/v1/detect-file-type
  job_creation_fee:
    amount: '0.002000'
    atomic_units: '2000'
    applies_to: POST /api/x402/v1/convert-file
    description: >-
      Small non-refundable job creation fee to reserve processing capacity and
      reduce spam.
  conversion_result_tiers:
    - rule: file_size_lte_10mb
      max_size_bytes: 10485760
      amount: '0.020000'
      atomic_units: '20000'
    - rule: file_size_gt_10mb_lte_40mb
      min_size_bytes: 10485761
      max_size_bytes: 41943040
      amount: '0.040000'
      atomic_units: '40000'
  maximum_file_size_bytes: 41943040

