openapi: 3.1.0
info:
  title: Bayline API
  version: 1.0.0
  description: Parking management platform API. Source of truth for all generated clients.
servers:
  - url: https://app.bodek.us/api/v1
    description: Staging

tags:
  - name: Bootstrap
  - name: Auth
  - name: Properties
  - name: Settings
  - name: Audit

paths:
  /bootstrap:
    get:
      tags: [Bootstrap]
      summary: Brand identity, public settings, and API metadata
      security: []
      responses:
        '200':
          description: Bootstrap payload
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Bootstrap'

  /auth/register:
    post:
      tags: [Auth]
      summary: Register a new account
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, email, password, password_confirmation]
              properties:
                name: { type: string, maxLength: 120 }
                email: { type: string, format: email }
                phone: { type: string, nullable: true }
                password: { type: string, minLength: 12 }
                password_confirmation: { type: string }
                timezone: { type: string, nullable: true }
                device_name: { type: string, nullable: true }
      responses:
        '201':
          description: Account created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSession'
        '422':
          $ref: '#/components/responses/Problem'

  /auth/login:
    post:
      tags: [Auth]
      summary: Sign in
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email: { type: string, format: email }
                password: { type: string }
                mfa_code: { type: string, minLength: 6, maxLength: 6, nullable: true }
                device_name: { type: string, nullable: true }
      responses:
        '200':
          description: Signed in
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSession'
        '401':
          $ref: '#/components/responses/Problem'
        '422':
          $ref: '#/components/responses/Problem'

  /auth/logout:
    post:
      tags: [Auth]
      summary: Revoke the current token
      responses:
        '204': { description: Signed out }

  /auth/me:
    get:
      tags: [Auth]
      summary: Current user with property memberships
      responses:
        '200':
          description: Current user
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/User'

  /auth/mfa:
    post:
      tags: [Auth]
      summary: Begin MFA enrollment
      responses:
        '200':
          description: Enrollment secret and otpauth URL
    delete:
      tags: [Auth]
      summary: Disable MFA
      responses:
        '204': { description: Disabled }

  /auth/mfa/confirm:
    post:
      tags: [Auth]
      summary: Confirm MFA enrollment and receive recovery codes
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [code]
              properties:
                code: { type: string, minLength: 6, maxLength: 6 }
      responses:
        '200': { description: Recovery codes }

  /properties:
    get:
      tags: [Properties]
      summary: List properties the current user belongs to
      responses:
        '200':
          description: Property list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/Property' }
    post:
      tags: [Properties]
      summary: Create a property
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PropertyInput' }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/Property' }

  /properties/{property}:
    parameters:
      - name: property
        in: path
        required: true
        schema: { type: integer }
    get:
      tags: [Properties]
      summary: Show a property
      responses:
        '200':
          description: Property
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/Property' }
        '403':
          $ref: '#/components/responses/Problem'
    patch:
      tags: [Properties]
      summary: Update a property
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PropertyInput' }
      responses:
        '200':
          description: Updated

  /properties/{property}/settings:
    parameters:
      - name: property
        in: path
        required: true
        schema: { type: integer }
    get:
      tags: [Settings]
      summary: Overridable settings with resolved values and admin bounds
      responses:
        '200':
          description: Setting list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/SettingDescriptor' }
    patch:
      tags: [Settings]
      summary: Set property overrides
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [settings]
              properties:
                settings:
                  type: object
                  additionalProperties: true
      responses:
        '200': { description: Applied values }
        '422':
          $ref: '#/components/responses/Problem'

  /properties/{property}/audit-log:
    parameters:
      - name: property
        in: path
        required: true
        schema: { type: integer }
      - name: cursor
        in: query
        schema: { type: string }
      - name: per_page
        in: query
        schema: { type: integer, maximum: 100 }
    get:
      tags: [Audit]
      summary: Property audit trail
      responses:
        '200':
          description: Audit entries

  /admin/settings:
    get:
      tags: [Settings]
      summary: All system settings with definitions and bounds
      responses:
        '200':
          description: Setting list
    patch:
      tags: [Settings]
      summary: Update system settings
      responses:
        '200': { description: Applied values }

  /admin/audit-log:
    get:
      tags: [Audit]
      summary: Global audit trail
      parameters:
        - name: action
          in: query
          schema: { type: string }
        - name: property_id
          in: query
          schema: { type: integer }
        - name: cursor
          in: query
          schema: { type: string }
      responses:
        '200':
          description: Audit entries

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
    cookieAuth:
      type: apiKey
      in: cookie
      name: bayline_session

  responses:
    Problem:
      description: RFC 7807 problem detail
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'

  schemas:
    Problem:
      type: object
      properties:
        type: { type: string }
        title: { type: string }
        status: { type: integer }
        detail: { type: string }
        instance: { type: string }
        errors:
          type: object
          additionalProperties:
            type: array
            items: { type: string }

    Bootstrap:
      type: object
      properties:
        brand:
          type: object
          properties:
            site_name: { type: string }
            site_tagline: { type: string }
            legal_entity_name: { type: string }
            domain_primary: { type: string }
            support_email: { type: string }
            support_phone: { type: string, nullable: true }
            billing_email: { type: string }
            mailing_address: { type: string, nullable: true }
            assets:
              type: object
              additionalProperties: { type: string }
            legal:
              type: object
              additionalProperties: { type: string }
            defaults:
              type: object
              properties:
                locale: { type: string }
                timezone: { type: string }
                currency: { type: string }
        settings:
          type: object
          additionalProperties: true
        api:
          type: object
          properties:
            version: { type: string }
            max_page_size: { type: integer }

    User:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        email: { type: string }
        phone: { type: string, nullable: true }
        locale: { type: string }
        timezone: { type: string }
        is_system_admin: { type: boolean }
        mfa_enabled: { type: boolean }
        email_verified: { type: boolean }
        phone_verified: { type: boolean }
        properties:
          type: array
          items: { $ref: '#/components/schemas/PropertyMembership' }
        created_at: { type: string, format: date-time }

    AuthSession:
      type: object
      properties:
        data:
          type: object
          properties:
            user: { $ref: '#/components/schemas/User' }
            token: { type: string, nullable: true }

    PropertyMembership:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        timezone: { type: string }
        currency: { type: string }
        status: { type: string }
        role:
          type: string
          enum: [owner, manager, enforcement, tenant]

    Property:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        legal_name: { type: string, nullable: true }
        address:
          type: object
          properties:
            line1: { type: string, nullable: true }
            line2: { type: string, nullable: true }
            city: { type: string, nullable: true }
            region: { type: string, nullable: true }
            postal_code: { type: string, nullable: true }
            country: { type: string }
        timezone: { type: string }
        currency: { type: string }
        status: { type: string }
        created_at: { type: string, format: date-time }

    PropertyInput:
      type: object
      required: [name]
      properties:
        name: { type: string, maxLength: 160 }
        legal_name: { type: string, nullable: true }
        address_line1: { type: string, nullable: true }
        address_line2: { type: string, nullable: true }
        city: { type: string, nullable: true }
        region: { type: string, nullable: true }
        postal_code: { type: string, nullable: true }
        country: { type: string, minLength: 2, maxLength: 2 }
        timezone: { type: string }
        currency: { type: string, minLength: 3, maxLength: 3 }

    SettingDescriptor:
      type: object
      properties:
        key: { type: string }
        label: { type: string }
        group: { type: string }
        type:
          type: string
          enum: [int, float, bool, string, enum, array]
        value: {}
        default: {}
        system_default: {}
        min: { type: number, nullable: true }
        max: { type: number, nullable: true }
        options:
          type: array
          items: { type: string }
        property_overridable: { type: boolean }
        overridden: { type: boolean }

security:
  - bearerAuth: []
  - cookieAuth: []
