openapi: 3.1.0
info:
  title: Zimun External API
  version: 0.1.0
  description: OAuth2-protected API for availability and appointment management.
servers:
  - url: https://api.zimun.online/api/v1
security:
  - oauth_client_credentials: [ ]

components:
  securitySchemes:
    oauth_client_credentials:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: /oauth/token
          scopes:
            availability:read: Read availability
            appointments:read: Read appointments
            appointments:write: Create/update appointments
            org:read: Read organization metadata
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code: { type: string }
            message: { type: string }
            details: { type: object }
    Organization:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        timezone: { type: string }
        address: { type: string }
    Service:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        duration_minutes: { type: integer }
    Staff:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
    Resource:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
    AvailabilitySlot:
      type: object
      properties:
        start: { type: string, format: date-time }
        end: { type: string, format: date-time }
        status: { type: string, enum: [available, held, booked] }
        staff_id: { type: string }
        service_id: { type: string }
    Appointment:
      type: object
      properties:
        id: { type: string }
        status: { type: string, enum: [scheduled, cancelled] }
        start: { type: string, format: date-time }
        end: { type: string, format: date-time }
        service_id: { type: string }
        staff_id: { type: string }
        customer:
          type: object
          properties:
            name: { type: string }
            email: { type: string }
            phone: { type: string }
    HoldRequest:
      type: object
      required: [service_id, start, end]
      properties:
        service_id: { type: string }
        staff_id: { type: string }
        start: { type: string, format: date-time }
        end: { type: string, format: date-time }
    HoldResponse:
      type: object
      properties:
        hold_id: { type: string }
        expires_at: { type: string, format: date-time }

paths:
  /organizations/me:
    get:
      tags: [Organization]
      security:
        - oauth_client_credentials: [org:read]
      responses:
        '200': { description: OK, content: { application/json: { schema: { $ref: '#/components/schemas/Organization' } } } }
        '401': { description: Unauthorized, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
  /services:
    get:
      tags: [Services]
      security:
        - oauth_client_credentials: [org:read]
      responses:
        '200': { description: OK, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Service' } } } } }
  /staff:
    get:
      tags: [Staff]
      security:
        - oauth_client_credentials: [org:read]
      responses:
        '200': { description: OK, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Staff' } } } } }
  /resources:
    get:
      tags: [Resources]
      security:
        - oauth_client_credentials: [org:read]
      responses:
        '200': { description: OK, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Resource' } } } } }
  /availability:
    get:
      tags: [Availability]
      security:
        - oauth_client_credentials: [availability:read]
      parameters:
        - in: query
          name: service_id
          required: true
          schema: { type: string }
        - in: query
          name: staff_id
          schema: { type: string }
        - in: query
          name: date
          schema: { type: string, format: date }
        - in: query
          name: days_ahead
          schema: { type: integer, minimum: 1, maximum: 30 }
      responses:
        '200': { description: OK, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/AvailabilitySlot' } } } } }
  /appointments/hold:
    post:
      tags: [Appointments]
      security:
        - oauth_client_credentials: [appointments:write]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/HoldRequest' }
      responses:
        '200': { description: Hold created, content: { application/json: { schema: { $ref: '#/components/schemas/HoldResponse' } } } }
  /holds/{hold_id}:
    delete:
      tags: [Appointments]
      security:
        - oauth_client_credentials: [appointments:write]
      parameters:
        - in: path
          name: hold_id
          required: true
          schema: { type: string }
      responses:
        '204': { description: Hold released }
  /appointments:
    get:
      tags: [Appointments]
      security:
        - oauth_client_credentials: [appointments:read]
      responses:
        '200': { description: OK, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Appointment' } } } } }
    post:
      tags: [Appointments]
      security:
        - oauth_client_credentials: [appointments:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hold_id, customer]
              properties:
                hold_id: { type: string }
                customer:
                  type: object
                  properties:
                    name: { type: string }
                    email: { type: string }
                    phone: { type: string }
                notes: { type: string }
      responses:
        '200': { description: Appointment confirmed, content: { application/json: { schema: { $ref: '#/components/schemas/Appointment' } } } }
        '409': { description: Conflict/expired hold, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
  /appointments/{id}:
    get:
      tags: [Appointments]
      security:
        - oauth_client_credentials: [appointments:read]
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200': { description: OK, content: { application/json: { schema: { $ref: '#/components/schemas/Appointment' } } } }
    patch:
      tags: [Appointments]
      security:
        - oauth_client_credentials: [appointments:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                start: { type: string, format: date-time }
                end: { type: string, format: date-time }
                status: { type: string, enum: [cancelled] }
      responses:
        '200': { description: Updated, content: { application/json: { schema: { $ref: '#/components/schemas/Appointment' } } } }
        '409': { description: Conflict, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
