openapi: 3.1.0
info:
  title: PennyOCR API
  description: Great OCR for pennies. $0.75 per 1,000 pages.
  version: 0.1.0
paths:
  /auth/request-code:
    post:
      summary: Request Code
      operationId: request_code_auth_request_code_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestCode'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /auth/verify:
    post:
      summary: Verify
      operationId: verify_auth_verify_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Verify'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /dashboard/me:
    get:
      summary: Me
      operationId: me_dashboard_me_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      security:
      - HTTPBearer: []
  /dashboard/keys:
    get:
      summary: List Keys
      operationId: list_keys_dashboard_keys_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      security:
      - HTTPBearer: []
    post:
      summary: Create Key
      operationId: create_key_dashboard_keys_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewKey'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /dashboard/keys/{key_id}:
    delete:
      summary: Revoke Key
      operationId: revoke_key_dashboard_keys__key_id__delete
      security:
      - HTTPBearer: []
      parameters:
      - name: key_id
        in: path
        required: true
        schema:
          type: string
          title: Key Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /dashboard/ocr:
    post:
      summary: Playground Ocr
      description: 'UI playground: OCR with your session (no API key juggling). Metered

        exactly like the API; the job is stored so it shows on your jobs page.'
      operationId: playground_ocr_dashboard_ocr_post
      security:
      - HTTPBearer: []
      parameters:
      - name: format
        in: query
        required: false
        schema:
          type: string
          default: markdown
          title: Format
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Body_playground_ocr_dashboard_ocr_post'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /dashboard/jobs:
    get:
      summary: List Jobs
      operationId: list_jobs_dashboard_jobs_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      security:
      - HTTPBearer: []
  /dashboard/jobs/{job_id}:
    get:
      summary: Get Job
      operationId: get_job_dashboard_jobs__job_id__get
      security:
      - HTTPBearer: []
      parameters:
      - name: job_id
        in: path
        required: true
        schema:
          type: string
          title: Job Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    delete:
      summary: Delete Job
      operationId: delete_job_dashboard_jobs__job_id__delete
      security:
      - HTTPBearer: []
      parameters:
      - name: job_id
        in: path
        required: true
        schema:
          type: string
          title: Job Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /dashboard/jobs/{job_id}/rating:
    post:
      summary: Rate Job
      operationId: rate_job_dashboard_jobs__job_id__rating_post
      security:
      - HTTPBearer: []
      parameters:
      - name: job_id
        in: path
        required: true
        schema:
          type: string
          title: Job Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Rating'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /dashboard/settings:
    post:
      summary: Update Settings
      description: 'store_results=false is zero-data-retention for API traffic: results
        are

        returned to the caller and discarded. Playground jobs are always stored

        (they exist to be viewed) and can be deleted from the jobs page.'
      operationId: update_settings_dashboard_settings_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SettingsBody'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /dashboard/usage:
    get:
      summary: Usage
      operationId: usage_dashboard_usage_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      security:
      - HTTPBearer: []
  /dashboard/usage/daily:
    get:
      summary: Usage Daily
      description: Pages per day, last 30 days (zero-filled client-side).
      operationId: usage_daily_dashboard_usage_daily_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      security:
      - HTTPBearer: []
  /dashboard/billing/checkout:
    post:
      summary: Create Checkout
      description: Buy a prepaid credit pack via one-time Stripe Checkout.
      operationId: create_checkout_dashboard_billing_checkout_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckoutBody'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /dashboard/billing/packs:
    get:
      summary: List Packs
      operationId: list_packs_dashboard_billing_packs_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
  /dashboard/billing/auto-reload:
    post:
      summary: Set Auto Reload
      description: Enable/disable automatic top-ups from the saved card when balance
        runs low.
      operationId: set_auto_reload_dashboard_billing_auto_reload_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AutoReloadBody'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /webhooks/stripe:
    post:
      summary: Stripe Webhook
      operationId: stripe_webhook_webhooks_stripe_post
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
  /v1/chat/completions:
    post:
      summary: Chat Completions
      operationId: chat_completions_v1_chat_completions_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatCompletionRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /v1/models:
    get:
      summary: List Models
      operationId: list_models_v1_models_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
  /v1/estimate:
    post:
      summary: Estimate
      description: "Page count + exact cost for a document URL. Free \u2014 nothing\
        \ is OCR'd."
      operationId: estimate_v1_estimate_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EstimateRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /v1/ocr/url:
    post:
      summary: Ocr Url
      description: OCR a document by URL, with page ranges and agent-safe spend guards.
      operationId: ocr_url_v1_ocr_url_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UrlOCRRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OCRResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - HTTPBearer: []
  /health:
    get:
      summary: Health
      operationId: health_health_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /v1/ocr:
    post:
      summary: Ocr
      description: "OCR a document (PDF, PNG, JPEG, WebP, TIFF). Billed per page.\n\
        \nformat=markdown (default): structured markdown with tables.\nformat=text:\
        \ plain text only \u2014 significantly faster."
      operationId: ocr_v1_ocr_post
      security:
      - HTTPBearer: []
      parameters:
      - name: format
        in: query
        required: false
        schema:
          type: string
          default: markdown
          title: Format
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Body_ocr_v1_ocr_post'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OCRResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    AutoReloadBody:
      properties:
        enabled:
          type: boolean
          title: Enabled
        pack:
          type: string
          title: Pack
          default: 10k
        threshold_pages:
          type: integer
          title: Threshold Pages
          default: 1000
      type: object
      required:
      - enabled
      title: AutoReloadBody
    Body_ocr_v1_ocr_post:
      properties:
        file:
          type: string
          contentMediaType: application/octet-stream
          title: File
      type: object
      required:
      - file
      title: Body_ocr_v1_ocr_post
    Body_playground_ocr_dashboard_ocr_post:
      properties:
        file:
          type: string
          contentMediaType: application/octet-stream
          title: File
      type: object
      required:
      - file
      title: Body_playground_ocr_dashboard_ocr_post
    ChatCompletionRequest:
      properties:
        model:
          type: string
          title: Model
          default: pennyocr-markdown
        messages:
          items: {}
          type: array
          title: Messages
        temperature:
          anyOf:
          - type: number
          - type: 'null'
          title: Temperature
        max_tokens:
          anyOf:
          - type: integer
          - type: 'null'
          title: Max Tokens
        stream:
          anyOf:
          - type: boolean
          - type: 'null'
          title: Stream
        top_p:
          anyOf:
          - type: number
          - type: 'null'
          title: Top P
        user:
          anyOf:
          - type: string
          - type: 'null'
          title: User
      additionalProperties: true
      type: object
      required:
      - messages
      title: ChatCompletionRequest
    CheckoutBody:
      properties:
        pack:
          type: string
          title: Pack
        return_origin:
          type: string
          title: Return Origin
          default: https://pennyocr.com
      type: object
      required:
      - pack
      title: CheckoutBody
    EstimateRequest:
      properties:
        url:
          type: string
          title: Url
        pages:
          anyOf:
          - type: string
          - type: 'null'
          title: Pages
      type: object
      required:
      - url
      title: EstimateRequest
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    HealthResponse:
      properties:
        status:
          type: string
          title: Status
          default: ok
        engine:
          type: string
          title: Engine
        model:
          type: string
          title: Model
      type: object
      required:
      - engine
      - model
      title: HealthResponse
    NewKey:
      properties:
        name:
          type: string
          title: Name
          default: default
      type: object
      title: NewKey
    OCRResponse:
      properties:
        id:
          type: string
          title: Id
          description: Request id, e.g. ocr_ab12cd34
        pages:
          type: integer
          title: Pages
        text:
          type: string
          title: Text
          description: Full extracted text, pages joined by form feeds
        page_results:
          items:
            $ref: '#/components/schemas/PageResult'
          type: array
          title: Page Results
        cost_usd:
          type: number
          title: Cost Usd
        model:
          type: string
          title: Model
      type: object
      required:
      - id
      - pages
      - text
      - page_results
      - cost_usd
      - model
      title: OCRResponse
    PageResult:
      properties:
        page:
          type: integer
          title: Page
          description: 1-indexed page number
        text:
          type: string
          title: Text
      type: object
      required:
      - page
      - text
      title: PageResult
    Rating:
      properties:
        rating:
          type: integer
          title: Rating
      type: object
      required:
      - rating
      title: Rating
    RequestCode:
      properties:
        email:
          type: string
          format: email
          title: Email
      type: object
      required:
      - email
      title: RequestCode
    SettingsBody:
      properties:
        store_results:
          type: boolean
          title: Store Results
      type: object
      required:
      - store_results
      title: SettingsBody
    UrlOCRRequest:
      properties:
        url:
          type: string
          title: Url
        format:
          type: string
          title: Format
          default: markdown
        pages:
          anyOf:
          - type: string
          - type: 'null'
          title: Pages
          description: 'Page range like ''1-20,25''. Default: all.'
        max_pages:
          anyOf:
          - type: integer
          - type: 'null'
          title: Max Pages
          description: Refuse (413) if selection exceeds this.
        max_cost_usd:
          anyOf:
          - type: number
          - type: 'null'
          title: Max Cost Usd
          description: Refuse (413) if cost would exceed this.
      type: object
      required:
      - url
      title: UrlOCRRequest
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    Verify:
      properties:
        email:
          type: string
          format: email
          title: Email
        code:
          type: string
          title: Code
      type: object
      required:
      - email
      - code
      title: Verify
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer
