openapi: 3.1.0
info:
  title: PLAYTEX Public Generation API
  version: v1
  description: Async public API for PLAYTEX PBR map, AI texture, and image-to-texture generation.
servers:
  - url: https://{project-ref}.supabase.co/functions/v1/playtex-api/v1
    variables:
      project-ref:
        default: your-project-ref
  - url: https://{project-ref}.functions.supabase.co/playtex-api/v1
    variables:
      project-ref:
        default: your-project-ref
security:
  - PlaytexApiKey: []
components:
  securitySchemes:
    PlaytexApiKey:
      type: http
      scheme: bearer
      bearerFormat: ptx_live
  schemas:
    ImageSource:
      type: object
      properties:
        url:
          type: string
          format: uri
          pattern: '^https://'
        data_url:
          type: string
          description: Base64 image data URL.
      oneOf:
        - required: [url]
        - required: [data_url]
    CreateJobResponse:
      type: object
      required: [id, status, poll_url, estimated_credits, credits_reserved]
      properties:
        id:
          type: string
          format: uuid
        status:
          const: queued
        poll_url:
          type: string
        estimated_credits:
          type: integer
          minimum: 0
          description: Credits estimated from the accepted request.
        credits_reserved:
          type: integer
          minimum: 0
          description: Credits reserved from the shared generation wallet when the job was queued.
    Job:
      type: object
      required: [id, status, kind, progress, created_at, outputs]
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum: [queued, running, succeeded, failed, canceled]
        kind:
          type: string
          enum: [pbr_map, ai_texture, image_to_texture]
        progress:
          type: integer
          minimum: 0
          maximum: 100
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        expires_at:
          type: string
          format: date-time
        request:
          type: object
        response:
          type: object
        usage:
          $ref: '#/components/schemas/JobUsage'
        outputs:
          type: array
          items:
            $ref: '#/components/schemas/Output'
        error:
          type: object
          nullable: true
          properties:
            code:
              type: string
            message:
              type: string
    JobUsage:
      type: object
      nullable: true
      properties:
        estimated_credits:
          type: integer
          minimum: 0
        credits_reserved:
          type: integer
          minimum: 0
        credits_consumed:
          type: integer
          minimum: 0
        credit_status:
          type: string
          enum: [none, reserved, consumed, refunded]
        credits:
          $ref: '#/components/schemas/CreditSnapshot'
    Output:
      type: object
      required: [type, filename, bucket, path, content_type]
      properties:
        type:
          type: string
        filename:
          type: string
        bucket:
          type: string
          default: api-outputs
        path:
          type: string
        content_type:
          type: string
        bytes:
          type: integer
          minimum: 0
        signed_url:
          type: string
          format: uri
          description: Present only on completed job reads. Expires after 1 hour.
    CreditSnapshot:
      type: object
      required: [monthly_limit, monthly_used, monthly_remaining, add_on_balance, total_available, reset_at]
      properties:
        monthly_limit:
          type: integer
        monthly_used:
          type: integer
        monthly_remaining:
          type: integer
        add_on_balance:
          type: integer
        total_available:
          type: integer
        reset_at:
          type: string
          format: date-time
          nullable: true
    CreditCost:
      type: object
      required: [kind, endpoint, method, scope, unit, cost_label, description]
      properties:
        kind:
          type: string
          enum: [pbr_map, ai_texture, image_to_texture]
        endpoint:
          type: string
        method:
          type: string
          enum: [POST]
        scope:
          type: string
        unit:
          type: string
        cost_label:
          type: string
        description:
          type: string
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
            message:
              type: string
            details: {}
    Usage:
      type: object
      required: [api_key, credits, credit_costs, rate_limit]
      properties:
        api_key:
          type: object
          properties:
            id:
              type: string
              format: uuid
            prefix:
              type: string
            scopes:
              type: array
              items:
                type: string
                enum:
                  - pbr:write
                  - ai_texture:write
                  - image_to_texture:write
                  - jobs:read
                  - usage:read
            rate_limit_per_minute:
              type: integer
            active_job_limit:
              type: integer
            active_jobs:
              type: integer
        credits:
          $ref: '#/components/schemas/CreditSnapshot'
        credit_costs:
          type: array
          items:
            $ref: '#/components/schemas/CreditCost'
          example:
          - kind: pbr_map
            endpoint: /v1/pbr-map-jobs
            method: POST
            scope: pbr:write
            unit: job
            cost_label: 1 credit per job
            description: Generates the requested PBR map stack and ZIP archive from one accepted job.
          - kind: ai_texture
            endpoint: /v1/ai-texture-jobs
            method: POST
            scope: ai_texture:write
            unit: variation
            cost_label: 1 credit per variation
            description: Reserves one shared generation credit for each requested texture variation.
          - kind: image_to_texture
            endpoint: /v1/image-to-texture-jobs
            method: POST
            scope: image_to_texture:write
            unit: output
            cost_label: 1 credit per output
            description: Reserves one shared generation credit for each requested image-to-texture output.
        rate_limit:
          type: object
          properties:
            allowed:
              type: boolean
            limit:
              type: integer
            used:
              type: integer
            reset_at:
              type: string
              format: date-time
paths:
  /pbr-map-jobs:
    post:
      summary: Create a PBR map generation job.
      description: Reserves 1 shared generation credit when the job is accepted.
      security:
        - PlaytexApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                source:
                  $ref: '#/components/schemas/ImageSource'
                mode:
                  type: string
                  enum: [image, hybrid, procedural]
                  default: image
                map_types:
                  type: array
                  items:
                    type: string
                    enum: [albedo, normal, metallic, emission, roughness, height, ao]
                settings:
                  type: object
                material_class:
                  type: string
                  enum: [dielectric, metal, coated]
                material_preset:
                  type: string
                export_resolution:
                  type: integer
                  minimum: 128
                  maximum: 8192
                seed:
                  type: integer
                surface_type:
                  type: string
      responses:
        '202':
          description: Job queued and credits reserved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateJobResponse'
        '4XX':
          description: Request rejected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /ai-texture-jobs:
    post:
      summary: Create an AI texture generation job.
      description: Reserves 1 shared generation credit per requested variation.
      security:
        - PlaytexApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                prompt:
                  type: string
                promptMode:
                  type: string
                  enum: [old, new]
                  default: new
                materialType:
                  type: string
                materialDescription:
                  type: string
                gameType:
                  type: string
                selectedColors:
                  type: array
                  items:
                    type: string
                    pattern: '^#[0-9a-fA-F]{6}$'
                style:
                  type: string
                seamless:
                  type: boolean
                seamBoost:
                  type: boolean
                variationCount:
                  type: integer
                  minimum: 1
                  maximum: 4
                  default: 1
                referenceImageDataUrl:
                  type: string
                editInstruction:
                  type: string
      responses:
        '202':
          description: Job queued and credits reserved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateJobResponse'
        '4XX':
          description: Request rejected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /image-to-texture-jobs:
    post:
      summary: Create an image-to-texture generation job.
      description: Reserves 1 shared generation credit per requested output. Top-level variationCount is preferred; settings.variationCount remains supported.
      security:
        - PlaytexApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                source:
                  $ref: '#/components/schemas/ImageSource'
                imageData:
                  type: string
                  description: Base64 image data URL.
                mode:
                  type: string
                  enum: [reimagine, extract]
                  default: reimagine
                variationCount:
                  type: integer
                  minimum: 1
                  maximum: 4
                  description: Preferred output count field. settings.variationCount and settings.variations are still accepted.
                settings:
                  type: object
                seamBoost:
                  type: boolean
      responses:
        '202':
          description: Job queued and credits reserved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateJobResponse'
        '4XX':
          description: Request rejected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /jobs/{id}:
    get:
      summary: Read an API generation job.
      description: Rate-limited read. Does not consume generation credits.
      security:
        - PlaytexApiKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Job state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '4XX':
          description: Request rejected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /usage:
    get:
      summary: Read current API key usage, wallet credits, and rate-limit state.
      description: Rate-limited read. Does not consume generation credits.
      security:
        - PlaytexApiKey: []
      responses:
        '200':
          description: Usage state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Usage'
        '4XX':
          description: Request rejected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
