---
openapi: 3.0.3
info:
  title: QRay API
  version: v1
  description: |
    Free QR code generation API. Create QR codes for 8 data types
    (url, text, wifi, vcard, email, phone, sms, geo) as PNG or SVG,
    with colors, gradients, logos and frames. Authenticate with the
    X-Api-Key header. Human-friendly docs: https://qray.io/en/developers
paths:
  "/api/v1/api_keys":
    get:
      summary: List your API keys
      tags:
      - API keys
      security:
      - api_key: []
      responses:
        '200':
          description: Keys returned (raw secret never included)
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/api_key"
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
    post:
      summary: Create an additional API key
      tags:
      - API keys
      security:
      - api_key: []
      parameters: []
      responses:
        '201':
          description: Key created (raw_key returned exactly once)
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/api_key"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: my-app production
  "/api/v1/api_keys/{id}":
    parameters:
    - name: id
      in: path
      description: The API key id
      required: true
      schema:
        type: integer
    delete:
      summary: Revoke an API key
      tags:
      - API keys
      security:
      - api_key: []
      description: The key authenticating the request cannot revoke itself.
      responses:
        '204':
          description: Revoked
        '422':
          description: Cannot delete the active key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
  "/api/v1/demo":
    post:
      summary: Generate a QR code without a key (keyless demo)
      tags:
      - QR codes
      security: []
      description: Returns the QR image binary directly (no base64). No auth; throttled
        to 10 requests/hour per IP. Same request body as /generate.
      parameters: []
      responses:
        '200':
          description: QR image returned
          content:
            image/png:
              schema:
                type: string
                format: binary
            image/svg+xml:
              schema:
                type: string
                format: binary
        '422':
          description: Unknown QR type
          content:
            image/png:
              schema:
                "$ref": "#/components/schemas/error"
            image/svg+xml:
              schema:
                "$ref": "#/components/schemas/error"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/generate_request"
  "/api/v1/health":
    get:
      summary: Service health and deployed version
      tags:
      - Account
      security: []
      description: Liveness + dependency checks. `git_sha` is the deployed commit
        — the API-compatibility gate reads it to diff against the live contract.
      responses:
        '200':
          description: Service healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  git_sha:
                    type: string
                    nullable: true
                    description: Deployed commit (Kamal version)
                  service:
                    type: string
                    example: qray-api
                  environment:
                    type: string
                    example: production
                  timestamp:
                    type: string
                    format: date-time
                  checks:
                    type: object
                    properties:
                      database:
                        type: boolean
                      redis:
                        type: boolean
                required:
                - status
                - service
                - checks
  "/api/v1/generate":
    post:
      summary: Generate a QR code
      tags:
      - QR codes
      security:
      - api_key: []
      description: Creates a QR code and returns a base64 image plus permanent URLs.
      parameters: []
      responses:
        '201':
          description: QR code created
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Daily quota for the plan
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests left today
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/generate_response"
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
        '422':
          description: Unknown QR type
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
        '429':
          description: Daily limit exceeded
          headers:
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: 0 when limited
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/generate_request"
  "/api/v1/qr_codes":
    get:
      summary: List your QR codes
      tags:
      - QR codes
      security:
      - api_key: []
      parameters:
      - name: page
        in: query
        required: false
        description: Page number (50 per page)
        schema:
          type: integer
      responses:
        '200':
          description: Paginated list
          content:
            application/json:
              schema:
                type: object
                properties:
                  page:
                    type: integer
                  per_page:
                    type: integer
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/qr_code"
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
  "/api/v1/qr_codes/{hash}":
    parameters:
    - name: hash
      in: path
      description: The QR code's content hash
      required: true
      schema:
        type: string
    get:
      summary: Get one QR code
      tags:
      - QR codes
      security:
      - api_key: []
      responses:
        '200':
          description: QR code metadata
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/qr_code"
        '404':
          description: Not found (or owned by another account)
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
    delete:
      summary: Delete a QR code
      tags:
      - QR codes
      security:
      - api_key: []
      responses:
        '204':
          description: Deleted
        '404':
          description: Not found
  "/api/v1/qr_codes/{hash}/image":
    parameters:
    - name: hash
      in: path
      description: The QR code's content hash
      required: true
      schema:
        type: string
    get:
      summary: Get the QR image binary
      tags:
      - QR codes
      security:
      - api_key: []
      description: Served from cache when warm, regenerated from stored parameters
        on a miss. The URL never expires.
      responses:
        '200':
          description: QR image binary
          content:
            image/png:
              schema:
                type: string
                format: binary
            image/svg+xml:
              schema:
                type: string
                format: binary
        '404':
          description: Not found
          content:
            image/png:
              schema:
                "$ref": "#/components/schemas/error"
            image/svg+xml:
              schema:
                "$ref": "#/components/schemas/error"
  "/api/v1/usage":
    get:
      summary: Current plan and usage
      tags:
      - Account
      security:
      - api_key: []
      responses:
        '200':
          description: Usage snapshot
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/usage"
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/error"
servers:
- url: https://qray.io
  description: Production
- url: http://localhost:3000
  description: Local development
components:
  securitySchemes:
    api_key:
      type: apiKey
      name: X-Api-Key
      in: header
      description: Per-key secret in the form qray_live_… . Create keys at /en/developers/keys.
  schemas:
    error:
      type: object
      properties:
        error:
          type: string
          enum:
          - missing_parameter
          - unauthorized
          - not_found
          - unknown_type
          - invalid_data
          - validation_failed
          - cannot_delete_active_key
          - rate_limited
          - service_unavailable
          example: unauthorized
        message:
          type: string
          nullable: true
      required:
      - error
    style:
      type: object
      description: Visual customization. All fields optional.
      properties:
        fg_color:
          type: string
          description: 'Foreground hex color, e.g. #111113.'
          example: "#111113"
        bg_color:
          type: string
          description: 'Background hex color, e.g. #FFFFFF.'
          example: "#FFFFFF"
        dot_style:
          type: string
          enum:
          - square
          - circle
          - rounded
        eye_style:
          type: string
          enum:
          - square
          - rounded
        logo_data:
          type: string
          description: 'Raw base64 PNG/JPG, no data: prefix (max ~500 KB).'
        gradient:
          type: object
          description: Two or more colors; overrides fg_color.
          properties:
            colors:
              type: array
              items:
                type: string
              example:
              - "#2563EB"
              - "#A3E635"
            angle:
              type: number
              example: 45
        frame:
          type: object
          description: PNG only; ignored for SVG.
          properties:
            type:
              type: string
              enum:
              - scan-me
              - custom-text
            text:
              type: string
            color:
              type: string
    generate_request:
      type: object
      required:
      - qr
      properties:
        qr:
          type: object
          required:
          - type
          - fields
          properties:
            type:
              type: string
              enum:
              - url
              - text
              - wifi
              - vcard
              - email
              - phone
              - sms
              - geo
            fields:
              type: object
              description: Type-specific fields (see the docs table).
              additionalProperties: true
            format:
              type: string
              enum:
              - png
              - svg
              default: png
            size:
              type: integer
              enum:
              - 256
              - 512
              - 1024
              - 2048
              default: 512
            style:
              "$ref": "#/components/schemas/style"
    generate_response:
      type: object
      properties:
        hash:
          type: string
          example: 1c9a7f2b40d861e3
        format:
          type: string
          example: png
        size:
          type: integer
          example: 512
        image:
          type: string
          description: Base64-encoded image bytes.
        image_url:
          type: string
          format: uri
        share_url:
          type: string
          format: uri
        url:
          type: string
    qr_code:
      type: object
      properties:
        hash:
          type: string
        type:
          type: string
        data:
          type: string
        format:
          type: string
        size:
          type: integer
        downloads_count:
          type: integer
        created_at:
          type: string
          format: date-time
        image_url:
          type: string
          format: uri
        share_url:
          type: string
          format: uri
        url:
          type: string
    api_key:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        plan:
          type: string
          enum:
          - free
          - pro
          - enterprise
        key:
          type: string
          description: Masked prefix for display.
        raw_key:
          type: string
          nullable: true
          description: Full secret; returned only once on create.
        requests_today:
          type: integer
        last_used_at:
          type: string
          format: date-time
          nullable: true
        active:
          type: boolean
        created_at:
          type: string
          format: date-time
    usage:
      type: object
      properties:
        plan:
          type: string
          enum:
          - free
          - pro
          - enterprise
        daily_limit:
          type: integer
        requests_today:
          type: integer
        remaining:
          type: integer
        requests_total:
          type: integer
        last_used_at:
          type: string
          format: date-time
          nullable: true
