openapi: 3.0.3
info:
  title: Scrapy.io Platform API
  version: '1.0.0'
  description: >
    Public Platform API for tool discovery, runs, datasets, and schedules.
    Tool execution remains on {publisher}.p.scrapy.infralyon.com.
servers:
  - url: https://api.scrapy.infralyon.com/v1
security:
  - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Scrapy.io API key (scrapy_api_...)
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [type, message, doc_url]
          properties:
            type:
              type: string
              enum:
                - unauthorized
                - forbidden
                - not_found
                - validation_error
                - insufficient_credits
                - conflict
                - rate_limit_exceeded
                - internal_error
            message:
              type: string
            doc_url:
              type: string
    CollectionMeta:
      type: object
      required: [items, total, offset, limit]
      properties:
        items:
          type: array
          items: {}
        total:
          type: integer
        offset:
          type: integer
        limit:
          type: integer
    Run:
      type: object
      properties:
        id: { type: string }
        kind:
          type: string
          enum: [api_sync, async_batch]
        status:
          type: string
          enum: [queued, running, succeeded, failed, cancelled, partial]
        toolId: { type: string }
        publisher: { type: string, nullable: true }
        toolSlug: { type: string, nullable: true }
        runName: { type: string, nullable: true }
        createdAt: { type: string }
        completedAt: { type: string, nullable: true }
    Schedule:
      type: object
      properties:
        id: { type: string }
        publisher: { type: string, nullable: true }
        slug: { type: string, nullable: true }
        runName: { type: string }
        timezone: { type: string }
        frequency: { type: string }
        date: { type: string }
        time: { type: string }
        isActive: { type: boolean }
paths:
  /tools:
    get:
      summary: List public tools
      parameters:
        - in: query
          name: q
          schema: { type: string }
        - in: query
          name: offset
          schema: { type: integer, default: 0 }
        - in: query
          name: limit
          schema: { type: integer, default: 20, maximum: 100 }
      responses:
        '200':
          description: Tool collection
        '401':
          description: Unauthorized
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
  /tools/{publisher}/{slug}:
    get:
      summary: Get a public tool
      parameters:
        - in: path
          name: publisher
          required: true
          schema: { type: string }
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Tool detail
        '404':
          description: Not found
  /runs:
    get:
      summary: List runs for the authenticated user
      parameters:
        - in: query
          name: kind
          schema: { type: string }
        - in: query
          name: toolId
          schema: { type: string }
        - in: query
          name: status
          schema: { type: string }
        - in: query
          name: offset
          schema: { type: integer }
        - in: query
          name: limit
          schema: { type: integer }
      responses:
        '200':
          description: Run collection
  /runs/{runId}:
    get:
      summary: Get a run
      parameters:
        - in: path
          name: runId
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Run
        '404':
          description: Not found
  /runs/{runId}/dataset/items:
    get:
      summary: Get dataset items for a run
      parameters:
        - in: path
          name: runId
          required: true
          schema: { type: string }
        - in: query
          name: offset
          schema: { type: integer }
        - in: query
          name: limit
          schema: { type: integer }
        - in: query
          name: format
          schema: { type: string }
      responses:
        '200':
          description: Dataset items or export body
  /schedules:
    get:
      summary: List schedules
      responses:
        '200':
          description: Schedule collection
    post:
      summary: Create schedule
      parameters:
        - in: header
          name: Idempotency-Key
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [publisher, slug, runName, timezone, frequency, date, time, inputs]
              properties:
                publisher: { type: string }
                slug: { type: string }
                runName: { type: string }
                timezone: { type: string }
                frequency: { type: string, enum: [one-time, repeatedly] }
                date: { type: string }
                time: { type: string }
                repeatFrequency: { type: string }
                inputs:
                  type: array
                  items: { type: string }
      responses:
        '201':
          description: Created
  /schedules/{scheduleId}:
    get:
      summary: Get schedule
      parameters:
        - in: path
          name: scheduleId
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Schedule
    patch:
      summary: Update schedule (isActive or notificationsEnabled)
      parameters:
        - in: path
          name: scheduleId
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                isActive: { type: boolean }
                notificationsEnabled: { type: boolean }
      responses:
        '200':
          description: Updated
    delete:
      summary: Soft-delete schedule
      parameters:
        - in: path
          name: scheduleId
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Deleted
