openapi: 3.0.3 # This matches the pattern

info:
    title: The Cork Conclave API
    description: API Specification.
    version: 0.0.1

tags:
    - name: auth
      description: Authentication
    - name: admins
      description: Admin users
    - name: events
      description: Event
    - name: webhooks
      description: Inbound webhooks

servers:
    - url: http://localhost:8087
      description: Main API server

security:
    - CookieAuth: []

paths:
    # -----------------------------
    # AUTH
    # -----------------------------

    /admins/auth/invite:
        post:
            tags: [auth]
            summary: Invite a new admin
            operationId: adminInvite
            security: []
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/AdminInviteRequest'
            responses:
                '200':
                    description: Invitation sent successfully
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AdminInviteResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /admin/auth/signup:
        post:
            tags: [auth]
            summary: Sign up an admin
            operationId: adminSignup
            security: []
            parameters:
                - name: mode
                  in: query
                  required: false
                  schema:
                      type: string
                      enum: [bff]
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/AdminSignupRequest'
            responses:
                '200':
                    description: Signup successful; tokens are set as HttpOnly cookies
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AuthCookieResponse'
                '201':
                    description: BFF mode token exchange (returns tokens as JSON; does not set cookies)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AdminSigninTokenResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

    /admin/auth/signin/request-link:
        post:
            tags: [auth]
            summary: Request a magic sign-in link by email
            operationId: adminSigninRequestLink
            security: []
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/AdminSigninRequestLinkRequest'
            responses:
                '200':
                    description: Always returned for valid request body; email is sent only if the account exists and is eligible
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AuthCookieResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

    /admin/auth/signin:
        get:
            tags: [auth]
            summary: Complete sign-in via magic link (sets HttpOnly cookies and redirects)
            operationId: adminSignin
            security: []
            parameters:
                - name: token
                  in: query
                  required: true
                  schema:
                      type: string
                - name: mode
                  in: query
                  required: false
                  schema:
                      type: string
                      enum: [bff]
            responses:
                '200':
                    description: BFF mode token exchange (returns tokens as JSON; does not redirect)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AdminSigninTokenResponse'
                '302':
                    description: Redirect to admin app (success or error Location)
                    headers:
                        Location:
                            required: true
                            schema:
                                type: string

    /admin/auth/refresh:
        post:
            tags: [auth]
            summary: Refresh access token
            operationId: authRefresh
            security: []
            parameters:
                - name: mode
                  in: query
                  required: false
                  schema:
                      type: string
                      enum: [bff]
            responses:
                '200':
                    description: New access token issued as HttpOnly cookie
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AuthCookieResponse'
                '201':
                    description: BFF mode refresh (returns new access token as JSON; does not set cookies)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AuthAccessTokenResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized (invalid or expired refresh token)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

    /admin/auth/session:
        get:
            tags: [auth]
            summary: Current session
            operationId: authSession
            responses:
                '200':
                    description: Session valid
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AdminAuthSessionResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

    /admin/auth/logout:
        post:
            tags: [auth]
            summary: Logout (invalidates tokens)
            operationId: authLogout
            responses:
                '204':
                    description: Logged out (tokens invalidated)
                '401':
                    description: Unauthorized (invalid or missing access cookie)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /admins/:
        get:
            tags: [admins]
            summary: List admin users
            operationId: listAdminUsers
            parameters:
                - name: page
                  in: query
                  required: false
                  description: Page number (1-based)
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
            responses:
                '200':
                    description: Paginated list of admin users
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AdminUsersListResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    # -----------------------------
    # Events
    # -----------------------------

    /events:
        post:
            tags: [events]
            summary: Create a new event
            operationId: createEvent
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateEventRequest'
            responses:
                '201':
                    description: Create Event
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/CreateEventResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

        get:
            tags: [events]
            summary: Get events
            operationId: getEvents
            parameters:
                - name: page
                  in: query
                  required: false
                  description: Page number (1-indexed)
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
                - name: q
                  in: query
                  required: false
                  description: Partial, case-insensitive name search
                  schema:
                      type: string
                - name: status
                  in: query
                  required: false
                  description: Filter by registration status (use checked_in for confirmed attendees that have checked in)
                  schema:
                      type: string
                      enum: [confirmed, pending_payment, cancelled, expired, checked_in]
                - name: date_from
                  in: query
                  required: false
                  description: Filter by event_date >= date_from (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: date_to
                  in: query
                  required: false
                  description: Filter by event_date <= date_to (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: sort_by
                  in: query
                  required: false
                  description: Sort criteria (default is created_at)
                  schema:
                      type: string
                      enum: [created_at, name]
                      default: created_at
                - name: sort_order
                  in: query
                  required: false
                  description: Sort order (default is desc; latest first when sorting by created_at)
                  schema:
                      type: string
                      enum: [asc, desc]
                      default: desc

            responses:
                '200':
                    description: Paginated list of events
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventsListResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/metrics:
        get:
            tags: [events]
            summary: Get event metrics (counts by status)
            operationId: getEventsMetrics
            responses:
                '200':
                    description: Event metrics (counts by status)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventsMetricsResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}:
        get:
            tags: [Events]
            summary: Get an event by ID
            operationId: getEventById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '200':
                    description: Event found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetEventByIdResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

        patch:
            tags: [events]
            summary: Update an event by ID
            operationId: updateEventById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdateEventRequest'
            responses:
                '200':
                    description: Event updated
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UpdateEventResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

        delete:
            tags: [events]
            summary: Delete an event by ID
            operationId: deleteEventById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '204':
                    description: Event deleted
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/registrations:
        get:
            tags: [events]
            summary: Get registrations for an event
            operationId: getEventRegistrations
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid

                - name: page
                  in: query
                  required: false
                  description: Page number (1-indexed)
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
                - name: q
                  in: query
                  required: false
                  description: Partial, case-insensitive name search
                  schema:
                      type: string
                - name: status
                  in: query
                  required: false
                  description: Filter by event status
                  schema:
                      type: string
                - name: date_from
                  in: query
                  required: false
                  description: Filter by event_date >= date_from (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: date_to
                  in: query
                  required: false
                  description: Filter by event_date <= date_to (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: sort_by
                  in: query
                  required: false
                  description: Sort criteria (default is created_at)
                  schema:
                      type: string
                      enum: [created_at, name]
                      default: created_at
                - name: sort_order
                  in: query
                  required: false
                  description: Sort order (default is desc; latest first when sorting by created_at)
                  schema:
                      type: string
                      enum: [asc, desc]
                      default: desc

            responses:
                '200':
                    description: List of registrations for the event
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventRegistrationsResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/activities:
        get:
            tags: [events]
            summary: Get activities for an event
            operationId: getEventActivities
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid

                - name: limit
                  in: query
                  required: false
                  description: Number of activities to return (cursor page size)
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 50

                - name: cursor_id
                  in: query
                  required: false
                  description: Cursor item ID (must be provided with cursor_created_at)
                  schema:
                      type: string
                      format: uuid

                - name: cursor_created_at
                  in: query
                  required: false
                  description: Cursor item created_at (RFC3339; must be provided with cursor_id)
                  schema:
                      type: string
                      format: date-time

            responses:
                '200':
                    description: List of activities for the event
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventActivitiesResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/checkin:
        post:
            tags: [events]
            summary: Check in an attendee for an event
            operationId: checkinEventAttendee
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/EventCheckinRequest'
            responses:
                '200':
                    description: Checked in successfully
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventCheckinResponse'
                '400':
                    description: Bad Request (ticket already used)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '404':
                    description: Not Found (ticket not found)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/metrics:
        get:
            tags: [events]
            summary: Get single event metrics
            operationId: getEventIDMetrics
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '200':
                    description: Event metrics
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetEventMetricsResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/finance-adjustment:
        patch:
            tags: [events]
            summary: Update event finance adjustment
            operationId: updateEventFinanceAdjustment
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdateEventFinanceAdjustmentRequest'
            responses:
                '200':
                    description: Updated event finance adjustment
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UpdateEventFinanceAdjustmentResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/gallery:
        post:
            tags: [events]
            summary: Create an event gallery item
            operationId: createEventGallery
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateEventGalleryRequest'
            responses:
                '201':
                    description: Gallery item created
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/CreateEventGalleryResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        get:
            tags: [events]
            summary: Get event gallery items
            operationId: getEventGallery
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '200':
                    description: List of gallery items for the event
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventGalleryListResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/gallery/{gallery_id}:
        patch:
            tags: [events]
            summary: Update an event gallery item by ID
            operationId: updateEventGalleryById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: gallery_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdateEventGalleryRequest'
            responses:
                '200':
                    description: Gallery item updated
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UpdateEventGalleryResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        delete:
            tags: [events]
            summary: Delete an event gallery item by ID
            operationId: deleteEventGalleryById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: gallery_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '204':
                    description: Gallery item deleted
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/questions:
        get:
            tags: [events]
            summary: List registration questions for an event
            operationId: getEventQuestions
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '200':
                    description: List of registration questions
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventQuestionListResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        post:
            tags: [events]
            summary: Create a registration question
            operationId: createEventQuestion
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateEventQuestionRequest'
            responses:
                '201':
                    description: Question created
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventQuestionResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/questions/reorder:
        patch:
            tags: [events]
            summary: Reorder registration questions
            operationId: reorderEventQuestions
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/ReorderEventQuestionsRequest'
            responses:
                '204':
                    description: Questions reordered
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/questions/{question_id}:
        patch:
            tags: [events]
            summary: Update a registration question
            operationId: updateEventQuestionById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: question_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdateEventQuestionRequest'
            responses:
                '200':
                    description: Question updated
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventQuestionResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        delete:
            tags: [events]
            summary: Delete a registration question
            operationId: deleteEventQuestionById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: question_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '204':
                    description: Question deleted
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/questions/{question_id}/options:
        post:
            tags: [events]
            summary: Create an option for a registration question
            operationId: createEventQuestionOption
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: question_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateEventQuestionOptionRequest'
            responses:
                '201':
                    description: Option created
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventQuestionOptionResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/questions/{question_id}/options/{option_id}:
        patch:
            tags: [events]
            summary: Update a registration question option
            operationId: updateEventQuestionOptionById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: question_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: option_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdateEventQuestionOptionRequest'
            responses:
                '200':
                    description: Option updated
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventQuestionOptionResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        delete:
            tags: [events]
            summary: Delete a registration question option
            operationId: deleteEventQuestionOptionById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: question_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: option_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '204':
                    description: Option deleted
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/expenses:
        post:
            tags: [events]
            summary: Create an expense for an event
            operationId: createEventExpense
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateEventExpenseRequest'
            responses:
                '201':
                    description: Expense created
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/CreateEventExpenseResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        get:
            tags: [events]
            summary: Get event expenses
            operationId: getEventExpenses
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: page
                  in: query
                  required: false
                  description: Page number (1-indexed)
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
                - name: q
                  in: query
                  required: false
                  description: Partial, case-insensitive search (title, description, vendor_name)
                  schema:
                      type: string
                - name: sort_by
                  in: query
                  required: false
                  description: Sort criteria (default is created_at)
                  schema:
                      type: string
                      enum: [created_at, expense_date, amount_in_kobo, title]
                      default: created_at
                - name: sort_order
                  in: query
                  required: false
                  description: Sort order (default is desc)
                  schema:
                      type: string
                      enum: [asc, desc]
                      default: desc
            responses:
                '200':
                    description: Paginated list of expenses for the event
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventExpensesListResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/expenses/total:
        get:
            tags: [events]
            summary: Get total expenses for an event (by currency)
            operationId: getEventExpensesTotal
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '200':
                    description: Total expenses grouped by currency
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventExpensesTotalResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /events/{id}/expenses/{expense_id}:
        patch:
            tags: [events]
            summary: Update an event expense by ID
            operationId: updateEventExpenseById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: expense_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdateEventExpenseRequest'
            responses:
                '200':
                    description: Expense updated
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UpdateEventExpenseResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'
        delete:
            tags: [events]
            summary: Delete an event expense by ID
            operationId: deleteEventExpenseById
            parameters:
                - name: id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
                - name: expense_id
                  in: path
                  required: true
                  schema:
                      type: string
                      format: uuid
            responses:
                '204':
                    description: Expense deleted
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    # -----------------------------
    # Public (no-auth)
    # -----------------------------

    /public/event:
        get:
            tags: [events]
            summary: Get the current active event (public)
            operationId: getPublicActiveEvent
            security: []
            responses:
                '200':
                    description: Active event
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetPublicActiveEventResponse'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /public/event/{slug}:
        get:
            tags: [events]
            summary: Get a completed event by slug (public)
            operationId: getPublicCompletedEventBySlug
            security: []
            parameters:
                - name: slug
                  in: path
                  required: true
                  schema:
                      type: string
            responses:
                '200':
                    description: Completed event details
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetPublicCompletedEventBySlugResponse'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /public/events:
        get:
            tags: [events]
            summary: Get public events
            operationId: getPublicEvents
            parameters:
                - name: page
                  in: query
                  required: false
                  description: Page number
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
                - name: q
                  in: query
                  required: false
                  description: Partial, case-insensitive name search
                  schema:
                      type: string
                - name: date_from
                  in: query
                  required: false
                  description: Filter by event_date >= date_from (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: date_to
                  in: query
                  required: false
                  description: Filter by event_date <= date_to (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: sort_by
                  in: query
                  required: false
                  description: Sort criteria (default is created_at)
                  schema:
                      type: string
                      enum: [created_at, event_date, name]
                      default: created_at
                - name: sort_order
                  in: query
                  required: false
                  description: Sort order (default is desc; latest first)
                  schema:
                      type: string
                      enum: [asc, desc]
                      default: desc

            responses:
                '200':
                    description: Paginated list of events
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventsListResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /public/event/register:
        post:
            tags: [events]
            summary: Register for the current active event
            operationId: registerForEvent
            security: []
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/RegisterForEventRequest'

            responses:
                '200':
                    description: Registration successful
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/RegisterForEventResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '404':
                    description: Not Found (no active event)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    # -----------------------------
    # Webhooks
    # -----------------------------

    /webhooks/ercas:
        post:
            tags: [webhooks]
            summary: Ercas payment webhook
            operationId: ercasWebhook
            security: []
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/ErcasWebhookPayload'
            responses:
                '200':
                    description: Webhook accepted
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/WebhookAckResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    # -----------------------------
    # Users
    # -----------------------------

    /users:
        get:
            tags: [users]
            summary: List users
            operationId: listUsers
            parameters:
                - name: page
                  in: query
                  required: false
                  description: Page number (1-indexed)
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
                - name: q
                  in: query
                  required: false
                  description: Partial, case-insensitive name search
                  schema:
                      type: string
                - name: date_from
                  in: query
                  required: false
                  description: Filter by event_date >= date_from (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: date_to
                  in: query
                  required: false
                  description: Filter by event_date <= date_to (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: sort_by
                  in: query
                  required: false
                  description: Sort criteria (default is created_at)
                  schema:
                      type: string
                      enum: [created_at, name]
                      default: created_at
                - name: sort_order
                  in: query
                  required: false
                  description: Sort order (default is desc; latest first when sorting by created_at)
                  schema:
                      type: string
                      enum: [asc, desc]
                      default: desc

            responses:
                '200':
                    description: Paginated list of users
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UsersListResponse'

                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    # -----------------------------
    # Metrics
    # -----------------------------

    /metrics/payments:
        get:
            tags: [metrics]
            summary: Get payment metrics
            operationId: getPaymentMetrics
            responses:
                '200':
                    description: Payment metrics
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/PaymentMetricsResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /metrics/payments/adjustment:
        patch:
            tags: [metrics]
            summary: Update payment metrics adjustment
            operationId: updatePaymentMetricsAdjustment
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/UpdatePaymentMetricsAdjustmentRequest'
            responses:
                '200':
                    description: Updated payment metrics
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/PaymentMetricsResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /metrics/members:
        get:
            tags: [metrics]
            summary: Get member metrics
            operationId: getMemberMetrics
            responses:
                '200':
                    description: Member metrics
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/MemberMetricsResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /metrics/tickets:
        get:
            tags: [metrics]
            summary: Get ticket metrics
            operationId: getTicketMetrics
            responses:
                '200':
                    description: Ticket metrics
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/TicketMetricsResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /metrics/attendance:
        get:
            tags: [metrics]
            summary: Get attendance metrics (current month)
            operationId: getAttendanceMetrics
            responses:
                '200':
                    description: Attendance metrics
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AttendanceMetricsResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /metrics/members/graph:
        get:
            tags: [metrics]
            summary: Get member graph metrics (new members by date)
            operationId: getMemberGraphMetrics
            parameters:
                - name: date_from
                  in: query
                  required: false
                  description: Filter by date >= date_from (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: date_to
                  in: query
                  required: false
                  description: Filter by date <= date_to (RFC3339)
                  schema:
                      type: string
                      format: date-time

            responses:
                '200':
                    description: Member graph metrics (new members by date)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/MemberGraphMetricsResponse'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /metrics/events/activity:
        get:
            tags: [metrics]
            summary: Get event activity metrics (registrations vs tickets)
            operationId: getEventsActivityMetrics
            parameters:
                - name: type
                  in: query
                  required: true
                  schema:
                      type: string
                      enum: [month, year]
                - name: value
                  in: query
                  required: true
                  description: ISO timestamp (RFC3339). Used to select the month or year.
                  schema:
                      type: string
                      format: date-time
            responses:
                '200':
                    description: Event activity metrics (registrations vs tickets)
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/EventActivityMetricsResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    # -----------------------------
    # Jobs
    # -----------------------------
    /jobs:
        get:
            tags: [jobs]
            summary: Get Jobs
            operationId: getJobs
            parameters:
                - name: page
                  in: query
                  required: false
                  description: Page number (1-indexed)
                  schema:
                      type: integer
                      minimum: 1
                      default: 1
                - name: per_page
                  in: query
                  required: false
                  description: Page size
                  schema:
                      type: integer
                      minimum: 1
                      maximum: 100
                      default: 10
                - name: status
                  in: query
                  required: false
                  description: Filter by status
                  schema:
                      type: string
                      enum: [pending, processing, completed, failed]
                - name: date_from
                  in: query
                  required: false
                  description: Filter by run_at >= date_from (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: date_to
                  in: query
                  required: false
                  description: Filter by run_at <= date_to (RFC3339)
                  schema:
                      type: string
                      format: date-time
                - name: sort_by
                  in: query
                  required: false
                  description: Sort criteria (default is created_at)
                  schema:
                      type: string
                      enum: [created_at]
                      default: created_at
                - name: sort_order
                  in: query
                  required: false
                  description: Sort order (default is desc; latest first when sorting by created_at)
                  schema:
                      type: string
                      enum: [asc, desc]
                      default: desc

            responses:
                '200':
                    description: Paginated list of jobs
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/JobsListResponse'
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'

                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'

                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

    /jobs/{id}:
        post:
            tags: [jobs]
            summary: Reschedule Job
            operationId: rescheduleJob
            parameters:
                - name: id
                  in: path
                  required: true
                  description: Job ID
                  schema:
                      $ref: '#/components/schemas/UUID'
            requestBody:
                required: false
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/RescheduleJobRequest'
            responses:
                '204':
                    description: Job rescheduled
                '400':
                    description: Bad Request
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorBadRequest'
                '401':
                    description: Unauthorized
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorUnauthorized'
                '404':
                    description: Not Found
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorNotFound'
                '500':
                    description: Internal Server Error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorInternalServerError'

components:
    securitySchemes:
        CookieAuth:
            type: apiKey
            in: cookie
            name: cc_access_token
            description: HttpOnly access token cookie set by magic-link signin, signup, and refresh

    schemas:
        UUID:
            type: string
            format: uuid

        Timestamp:
            type: string
            format: date-time

        ErrorBadRequest:
            type: object
            required: [code, message]
            properties:
                code:
                    type: integer
                    example: 400
                message:
                    type: string
                    example: Bad request

        ErrorUnauthorized:
            type: object
            required: [code, message]
            properties:
                code:
                    type: integer
                    example: 401
                message:
                    type: string
                    example: Unauthorized

        ErrorForbidden:
            type: object
            required: [code, message]
            properties:
                code:
                    type: integer
                    example: 403
                message:
                    type: string
                    example: Forbidden

        ErrorNotFound:
            type: object
            required: [code, message]
            properties:
                code:
                    type: integer
                    example: 404
                message:
                    type: string
                    example: Not found

        ErrorInternalServerError:
            type: object
            required: [code, message]
            properties:
                code:
                    type: integer
                    example: 500
                message:
                    type: string
                    example: Internal server error

        # -----------------------------
        # AUTH SCHEMAS
        # -----------------------------
        AdminInviteRequest:
            type: object
            required: [email, name]
            properties:
                email:
                    type: string
                    format: email
                    minLength: 1
                    maxLength: 255
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
            additionalProperties: false

        AdminInviteResponse:
            type: object
            required: [message]
            properties:
                message:
                    type: string
                    description: Success Message

            additionalProperties: false

        AdminSigninRequestLinkRequest:
            type: object
            required: [email]
            properties:
                email:
                    type: string
                    format: email
                    minLength: 1
                    maxLength: 255
            additionalProperties: false

        AuthCookieResponse:
            type: object
            required: [ok]
            properties:
                ok:
                    type: boolean
            additionalProperties: false

        AdminSigninTokenResponse:
            type: object
            required: [access_token, refresh_token]
            properties:
                access_token:
                    type: string
                    minLength: 1
                refresh_token:
                    type: string
                    minLength: 1
            additionalProperties: false

        AuthAccessTokenResponse:
            type: object
            required: [access_token]
            properties:
                access_token:
                    type: string
                    minLength: 1
            additionalProperties: false

        AdminSignupRequest:
            type: object
            required: [token]
            properties:
                token:
                    type: string
            additionalProperties: false

        AdminAuthSessionResponse:
            type: object
            required: [user_id, email, name]
            properties:
                user_id:
                    $ref: '#/components/schemas/UUID'
                email:
                    type: string
                    format: email
                    minLength: 1
                    maxLength: 255
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
            additionalProperties: false

        AdminUserListItem:
            type: object
            required: [id, name, email, verified_at, created_at]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                email:
                    type: string
                    format: email
                    minLength: 1
                    maxLength: 255
                verified_at:
                    $ref: '#/components/schemas/Timestamp'
                created_at:
                    $ref: '#/components/schemas/Timestamp'
            additionalProperties: false

        ListMeta:
            type: object
            required: [total, page, per_page, total_pages]
            properties:
                total:
                    type: integer
                    minimum: 0
                    example: 1
                page:
                    type: integer
                    minimum: 1
                    example: 1
                per_page:
                    type: integer
                    minimum: 1
                    example: 10
                total_pages:
                    type: integer
                    minimum: 0
                    example: 1
            additionalProperties: false

        AdminUsersListResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/AdminUserListItem'
                meta:
                    $ref: '#/components/schemas/ListMeta'
            additionalProperties: false

        # -----------------------------
        # EVENTS SCHEMAS
        # -----------------------------
        EventQuestionType:
            type: string
            enum: [yes_no, text, single_choice, multiple_choice]

        CreateEventQuestionOptionInput:
            type: object
            required: [value]
            properties:
                value:
                    type: string
                    minLength: 1
                sort_order:
                    type: integer
                    format: int32
            additionalProperties: false

        CreateEventQuestionInput:
            type: object
            required: [question, type]
            properties:
                question:
                    type: string
                    minLength: 1
                type:
                    $ref: '#/components/schemas/EventQuestionType'
                is_required:
                    type: boolean
                    default: false
                sort_order:
                    type: integer
                    format: int32
                options:
                    type: array
                    items:
                        $ref: '#/components/schemas/CreateEventQuestionOptionInput'
                    description: Required for single_choice and multiple_choice (min 2). Must be empty or omitted for text and yes_no.
            additionalProperties: false

        EventQuestionOption:
            type: object
            required: [id, question_id, value, sort_order]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                question_id:
                    $ref: '#/components/schemas/UUID'
                value:
                    type: string
                sort_order:
                    type: integer
                    format: int32
            additionalProperties: false

        EventQuestion:
            type: object
            required: [id, event_id, question, type, is_required, sort_order, options]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                event_id:
                    $ref: '#/components/schemas/UUID'
                question:
                    type: string
                type:
                    $ref: '#/components/schemas/EventQuestionType'
                is_required:
                    type: boolean
                sort_order:
                    type: integer
                    format: int32
                options:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventQuestionOption'
            additionalProperties: false

        CreateEventQuestionRequest:
            $ref: '#/components/schemas/CreateEventQuestionInput'

        UpdateEventQuestionRequest:
            type: object
            required: [question, type, is_required]
            properties:
                question:
                    type: string
                    minLength: 1
                type:
                    $ref: '#/components/schemas/EventQuestionType'
                is_required:
                    type: boolean
                sort_order:
                    type: integer
                    format: int32
            additionalProperties: false

        CreateEventQuestionOptionRequest:
            type: object
            required: [value]
            properties:
                value:
                    type: string
                    minLength: 1
                sort_order:
                    type: integer
                    format: int32
            additionalProperties: false

        UpdateEventQuestionOptionRequest:
            type: object
            required: [value]
            properties:
                value:
                    type: string
                    minLength: 1
                sort_order:
                    type: integer
                    format: int32
            additionalProperties: false

        ReorderEventQuestionsItem:
            type: object
            required: [id, sort_order]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                sort_order:
                    type: integer
                    format: int32
            additionalProperties: false

        ReorderEventQuestionsRequest:
            type: object
            required: [items]
            properties:
                items:
                    type: array
                    minItems: 1
                    items:
                        $ref: '#/components/schemas/ReorderEventQuestionsItem'
            additionalProperties: false

        EventQuestionListResponse:
            type: object
            required: [data]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventQuestion'
            additionalProperties: false

        EventQuestionResponse:
            type: object
            required: [question]
            properties:
                question:
                    $ref: '#/components/schemas/EventQuestion'
            additionalProperties: false

        EventQuestionOptionResponse:
            type: object
            required: [option]
            properties:
                option:
                    $ref: '#/components/schemas/EventQuestionOption'
            additionalProperties: false

        CreateEventRequest:
            type: object
            required: [name, event_date, venue_name, venue_address, registration_opens_at]
            properties:
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                description:
                    type: string
                    minLength: 1
                    maxLength: 255
                welcome_text:
                    type: string
                dress_code:
                    type: string
                entry_fee:
                    type: string
                event_date:
                    type: string
                venue_name:
                    type: string
                venue_address:
                    type: string
                amount_in_kobo:
                    type: string
                    description: Ticket price in kobo as a decimal string; omit to store 0 (free event).
                registration_opens_at:
                    type: string
                registration_closes_at:
                    type: string
                image_url:
                    type: string
                    format: uri
                questions:
                    type: array
                    items:
                        $ref: '#/components/schemas/CreateEventQuestionInput'
            additionalProperties: false

        CreateEventResponse:
            type: object
            required:
                [
                    id,
                    name,
                    slug,
                    description,
                    welcome_text,
                    dress_code,
                    entry_fee,
                    event_date,
                    venue_name,
                    venue_address,
                    amount_in_kobo,
                    status,
                    registration_opens_at,
                    registration_closes_at,
                    created_at,
                ]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                slug:
                    type: string
                description:
                    type: string
                    minLength: 1
                    maxLength: 255
                welcome_text:
                    type: string
                    nullable: true
                dress_code:
                    type: string
                    nullable: true
                entry_fee:
                    type: string
                    nullable: true
                event_date:
                    type: string
                venue_name:
                    type: string
                venue_address:
                    type: string
                amount_in_kobo:
                    type: string
                status:
                    type: string
                registration_opens_at:
                    type: string
                registration_closes_at:
                    type: string
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                image_url:
                    type: string
                    format: uri
            additionalProperties: false

        GetEventsResponse:
            type: object
            required: [event]
            properties:
                event:
                    type: array
                    items:
                        $ref: '#/components/schemas/GetEventsResponseItem'
            additionalProperties: false

        GetEventsResponseItem:
            type: object
            required:
                [
                    id,
                    name,
                    slug,
                    description,
                    welcome_text,
                    dress_code,
                    entry_fee,
                    event_date,
                    venue_name,
                    venue_address,
                    amount_in_kobo,
                    status,
                    registration_opens_at,
                    registration_closes_at,
                    created_at,
                ]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                slug:
                    type: string
                description:
                    type: string
                    minLength: 1
                    maxLength: 255
                welcome_text:
                    type: string
                    nullable: true
                dress_code:
                    type: string
                    nullable: true
                entry_fee:
                    type: string
                    nullable: true
                event_date:
                    type: string
                venue_name:
                    type: string
                venue_address:
                    type: string
                amount_in_kobo:
                    type: string
                status:
                    type: string
                checked_in_count:
                    type: integer
                    minimum: 0
                registration_opens_at:
                    type: string
                registration_closes_at:
                    type: string
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                image_url:
                    type: string
                    format: uri

        GetEventByIdResponse:
            type: object
            required: [event]
            properties:
                event:
                    $ref: '#/components/schemas/GetEventsResponseItem'
                questions:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventQuestion'
                    description: Registration questions for this event, ordered by sort_order.
            additionalProperties: false

        UpdateEventRequest:
            type: object
            properties:
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                description:
                    type: string
                    minLength: 1
                    maxLength: 255
                welcome_text:
                    type: string
                    nullable: true
                dress_code:
                    type: string
                    nullable: true
                entry_fee:
                    type: string
                    nullable: true
                event_date:
                    type: string
                    format: date-time
                venue_name:
                    type: string
                    minLength: 1
                venue_address:
                    type: string
                    minLength: 1
                amount_in_kobo:
                    type: string
                    minLength: 1
                status:
                    type: string
                    enum: [draft, active, closed, completed, cancelled]
                registration_opens_at:
                    type: string
                    format: date-time
                registration_closes_at:
                    type: string
                    format: date-time
                image_url:
                    type: string
                    format: uri
            additionalProperties: false

        UpdateEventResponse:
            type: object
            required: [event]
            properties:
                event:
                    $ref: '#/components/schemas/GetEventsResponseItem'
            additionalProperties: false

        EventsListResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/GetEventsResponseItem'
                meta:
                    $ref: '#/components/schemas/ListMeta'
            additionalProperties: false

        EventGallery:
            type: object
            required: [id, event_id, url, created_at, updated_at]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                event_id:
                    $ref: '#/components/schemas/UUID'
                url:
                    type: string
                    format: uri
                    minLength: 1
                caption:
                    type: string
                    nullable: true
                alt:
                    type: string
                    nullable: true
                public_id:
                    type: string
                    nullable: true
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                updated_at:
                    $ref: '#/components/schemas/Timestamp'
            additionalProperties: false

        CreateEventGalleryRequest:
            type: object
            required: [url]
            properties:
                url:
                    type: string
                    format: uri
                    minLength: 1
                caption:
                    type: string
                    nullable: true
                alt:
                    type: string
                    nullable: true
                public_id:
                    type: string
                    nullable: true
            additionalProperties: false

        CreateEventGalleryResponse:
            type: object
            required: [gallery]
            properties:
                gallery:
                    $ref: '#/components/schemas/EventGallery'
            additionalProperties: false

        EventGalleryListResponse:
            type: object
            required: [data]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventGallery'
            additionalProperties: false

        UpdateEventGalleryRequest:
            type: object
            required: [url]
            properties:
                url:
                    type: string
                    format: uri
                    minLength: 1
                caption:
                    type: string
                    nullable: true
                alt:
                    type: string
                    nullable: true
                public_id:
                    type: string
                    nullable: true
            additionalProperties: false

        EventExpense:
            type: object
            required:
                [
                    id,
                    event_id,
                    title,
                    amount_in_kobo,
                    currency,
                    created_at,
                    updated_at,
                ]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                event_id:
                    $ref: '#/components/schemas/UUID'
                title:
                    type: string
                    minLength: 1
                    maxLength: 255
                description:
                    type: string
                    nullable: true
                amount_in_kobo:
                    type: integer
                    minimum: 0
                currency:
                    type: string
                    minLength: 1
                category:
                    type: string
                    nullable: true
                expense_date:
                    type: string
                    format: date-time
                    nullable: true
                vendor_name:
                    type: string
                    nullable: true
                receipt_url:
                    type: string
                    format: uri
                    nullable: true
                paid_by:
                    type: string
                    nullable: true
                payment_method:
                    type: string
                    nullable: true
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                updated_at:
                    $ref: '#/components/schemas/Timestamp'
            additionalProperties: false

        CreateEventExpenseRequest:
            type: object
            required: [title, amount_in_kobo]
            properties:
                title:
                    type: string
                    minLength: 1
                    maxLength: 255
                description:
                    type: string
                    nullable: true
                amount_in_kobo:
                    type: integer
                    minimum: 0
                currency:
                    type: string
                    minLength: 1
                    default: NGN
                category:
                    type: string
                    nullable: true
                expense_date:
                    type: string
                    format: date-time
                    nullable: true
                vendor_name:
                    type: string
                    nullable: true
                receipt_url:
                    type: string
                    format: uri
                    nullable: true
                paid_by:
                    type: string
                    nullable: true
                payment_method:
                    type: string
                    nullable: true
            additionalProperties: false

        CreateEventExpenseResponse:
            type: object
            required: [expense]
            properties:
                expense:
                    $ref: '#/components/schemas/EventExpense'
            additionalProperties: false

        UpdateEventExpenseRequest:
            type: object
            properties:
                title:
                    type: string
                    minLength: 1
                    maxLength: 255
                description:
                    type: string
                    nullable: true
                amount_in_kobo:
                    type: integer
                    minimum: 0
                currency:
                    type: string
                    minLength: 1
                category:
                    type: string
                    nullable: true
                expense_date:
                    type: string
                    format: date-time
                    nullable: true
                vendor_name:
                    type: string
                    nullable: true
                receipt_url:
                    type: string
                    format: uri
                    nullable: true
                paid_by:
                    type: string
                    nullable: true
                payment_method:
                    type: string
                    nullable: true
            additionalProperties: false

        UpdateEventExpenseResponse:
            type: object
            required: [expense]
            properties:
                expense:
                    $ref: '#/components/schemas/EventExpense'
            additionalProperties: false

        EventExpensesListResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventExpense'
                meta:
                    $ref: '#/components/schemas/ListMeta'
            additionalProperties: false

        EventExpensesTotalItem:
            type: object
            required: [currency, total_amount_in_kobo]
            properties:
                currency:
                    type: string
                    minLength: 1
                total_amount_in_kobo:
                    type: integer
                    minimum: 0
            additionalProperties: false

        EventExpensesTotalResponse:
            type: object
            required: [data]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventExpensesTotalItem'
            additionalProperties: false

        UpdateEventGalleryResponse:
            type: object
            required: [gallery]
            properties:
                gallery:
                    $ref: '#/components/schemas/EventGallery'
            additionalProperties: false

        PublicEventGalleryItem:
            type: object
            required: [url]
            properties:
                url:
                    type: string
                    format: uri
                    minLength: 1
                caption:
                    type: string
                    nullable: true
                alt:
                    type: string
                    nullable: true
            additionalProperties: false

        PublicCompletedEventDetails:
            type: object
            required: [id, name, slug, description, event_date, venue_name, venue_address, amount_in_kobo]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                slug:
                    type: string
                    minLength: 1
                description:
                    type: string
                event_date:
                    type: string
                    format: date-time
                venue_name:
                    type: string
                venue_address:
                    type: string
                amount_in_kobo:
                    type: string
                image_url:
                    type: string
                    format: uri
                    nullable: true
                dress_code:
                    type: string
                    nullable: true
            additionalProperties: false

        GetPublicCompletedEventBySlugResponse:
            type: object
            required: [event, gallery, gallery_count]
            properties:
                event:
                    $ref: '#/components/schemas/PublicCompletedEventDetails'
                gallery:
                    type: array
                    items:
                        $ref: '#/components/schemas/PublicEventGalleryItem'
                gallery_count:
                    type: integer
                    minimum: 0
            additionalProperties: false

        PublicActiveEvent:
            type: object
            required: [id, slug, name, description, event_date, created_by, amount_in_kobo]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                slug:
                    type: string
                    minLength: 1
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                description:
                    type: string
                event_date:
                    type: string
                amount_in_kobo:
                    type: string
                image_url:
                    type: string
                    format: uri
                registration_opens_at:
                    type: string
                    format: date-time
                    description: When registration opens; omit if unrestricted. Public UI requires now >= this time to register.
                registration_closes_at:
                    type: string
                    format: date-time
                    description: When registration closes; omit or empty to rely on event date only for “past” UI.
            additionalProperties: false

        GetPublicActiveEventResponse:
            type: object
            required: [event]
            properties:
                event:
                    $ref: '#/components/schemas/PublicActiveEvent'
                questions:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventQuestion'
            additionalProperties: false

        RegisterForEventTicketResponseInput:
            type: object
            required: [question_id]
            properties:
                question_id:
                    $ref: '#/components/schemas/UUID'
                answer_text:
                    type: string
                answer_options:
                    type: array
                    items:
                        type: string
            additionalProperties: false

        RegisterForEventTicketInput:
            type: object
            required: [name, email, phone_number]
            properties:
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                email:
                    type: string
                    format: email
                    minLength: 1
                    maxLength: 255
                phone_number:
                    type: string
                    minLength: 1
                    maxLength: 32
                responses:
                    type: array
                    items:
                        $ref: '#/components/schemas/RegisterForEventTicketResponseInput'
            additionalProperties: false

        RegisterForEventRequest:
            type: object
            required: [tickets]
            properties:
                tickets:
                    type: array
                    minItems: 1
                    maxItems: 10
                    items:
                        $ref: '#/components/schemas/RegisterForEventTicketInput'
            additionalProperties: false

        EventsMetricsData:
            type: object
            required: [draft, active, closed, completed, cancelled]
            properties:
                draft:
                    type: integer
                    minimum: 0
                    example: 3
                active:
                    type: integer
                    minimum: 0
                    example: 0
                closed:
                    type: integer
                    minimum: 0
                    example: 0
                completed:
                    type: integer
                    minimum: 0
                    example: 2
                cancelled:
                    type: integer
                    minimum: 0
                    example: 0
            additionalProperties: false

        EventsMetricsResponse:
            type: object
            required: [data]
            properties:
                data:
                    $ref: '#/components/schemas/EventsMetricsData'
            additionalProperties: false

        RegisterForEventResponse:
            type: object
            properties:
                checkout_url:
                    type: string
                    format: uri
                    description: Ercas checkout URL for paid events; omitted or empty for free (zero-amount) registration.
            additionalProperties: false

        EventRegistrationsResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventRegistrationItem'
                meta:
                    $ref: '#/components/schemas/ListMeta'
            additionalProperties: false

        EventRegistrationQuestionResponse:
            type: object
            required: [question_id, question, type, sort_order]
            properties:
                question_id:
                    $ref: '#/components/schemas/UUID'
                question:
                    type: string
                type:
                    $ref: '#/components/schemas/EventQuestionType'
                sort_order:
                    type: integer
                    format: int32
                answer_text:
                    type: string
                answer_options:
                    type: array
                    items:
                        type: string
                options:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventQuestionOption'
                    description: Question options for resolving choice values to labels
            additionalProperties: false

        EventRegistrationItem:
            type: object
            required: [id, name, email, user_id, status, created_at]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                email:
                    type: string
                user_id:
                    $ref: '#/components/schemas/UUID'
                status:
                    type: string
                confirmed_at:
                    $ref: '#/components/schemas/Timestamp'
                checked_in_at:
                    $ref: '#/components/schemas/Timestamp'
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                responses:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventRegistrationQuestionResponse'
                    description: Answered registration questions only, ordered by sort_order

        EventActivitiesResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/EventActivityItem'
                meta:
                    $ref: '#/components/schemas/EventActivitiesMeta'
            additionalProperties: false

        EventActivitiesMeta:
            type: object
            required: [limit, has_more]
            properties:
                limit:
                    type: integer
                    minimum: 1
                    example: 20
                has_more:
                    type: boolean
                    example: true
                next_cursor:
                    $ref: '#/components/schemas/EventActivitiesCursor'
            additionalProperties: false

        EventActivitiesCursor:
            type: object
            required: [id, created_at]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                created_at:
                    $ref: '#/components/schemas/Timestamp'
            additionalProperties: false

        EventCheckinRequest:
            type: object
            required: [access_code]
            properties:
                access_code:
                    type: string
                    minLength: 1
            additionalProperties: false

        EventCheckinResponse:
            type: object
            required: [name, ticket_id, checkin_at]
            properties:
                name:
                    type: string
                ticket_id:
                    $ref: '#/components/schemas/UUID'
                checkin_at:
                    $ref: '#/components/schemas/Timestamp'

        EventActivityItem:
            type: object
            required: [id, type, title, description, created_at]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                type:
                    type: string
                    example: registration_created
                title:
                    type: string
                    minLength: 1
                description:
                    type: string
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                updated_at:
                    $ref: '#/components/schemas/Timestamp'
            additionalProperties: false

        # -----------------------------
        # WEBHOOKS SCHEMAS
        # -----------------------------
        ErcasWebhookPayload:
            type: object
            required: [transaction_reference, status]
            properties:
                amount:
                    type: number
                transaction_reference:
                    type: string
                    minLength: 1
                payment_reference:
                    type: string
                type:
                    type: string
                service_type:
                    type: string
                settlement_amount:
                    type: number
                fee_bearer:
                    type: string
                fee:
                    type: number
                channel:
                    type: string
                status:
                    type: string
                    minLength: 1
                currency:
                    type: string
                merchant_name:
                    type: string
                customer_account_name:
                    type: string
                    nullable: true
                metadata:
                    description: 'Ercas metadata can be an object or a stringified JSON blob; accept raw JSON.'
                    x-go-type: json.RawMessage
            additionalProperties: false

        WebhookAckResponse:
            type: object
            required: [message]
            properties:
                message:
                    type: string
            additionalProperties: false

        UsersListResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/UserListItem'
                meta:
                    $ref: '#/components/schemas/ListMeta'
            additionalProperties: false

        UserListItem:
            type: object
            required: [id, name, email, phone_number, verified_at, created_at, number_of_events]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                    minLength: 1
                    maxLength: 255
                email:
                    type: string
                    minLength: 1
                    maxLength: 255
                phone_number:
                    type: string
                    minLength: 1
                    maxLength: 20
                verified_at:
                    $ref: '#/components/schemas/Timestamp'
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                number_of_events:
                    type: integer
                    minimum: 0

        PaymentMetricsResponse:
            type: object
            required: [total, payments_total, previous_revenue_in_kobo, total_revenue_in_kobo]
            properties:
                payments_total:
                    type: integer
                    minimum: 0
                    example: 100
                    description: Sum of successful payments only (kobo)
                previous_revenue_in_kobo:
                    type: integer
                    minimum: 0
                    example: 0
                    description: Platform opening revenue adjustment (kobo)
                total_revenue_in_kobo:
                    type: integer
                    minimum: 0
                    example: 100
                    description: payments_total + previous_revenue_in_kobo (kobo)
                total:
                    type: integer
                    minimum: 0
                    example: 100
                    description: Backwards-compatible alias of payments_total (kobo)
            additionalProperties: false

        UpdatePaymentMetricsAdjustmentRequest:
            type: object
            required: [previous_revenue_in_kobo]
            properties:
                previous_revenue_in_kobo:
                    type: integer
                    minimum: 0
            additionalProperties: false

        MemberMetricsResponse:
            type: object
            required: [total, delta]
            properties:
                total:
                    type: integer
                    minimum: 0
                    example: 50
                delta:
                    type: number
                    example: 5
            additionalProperties: false

        TicketMetricsResponse:
            type: object
            required: [total, delta]
            properties:
                total:
                    type: integer
                    minimum: 0
                    example: 200
                delta:
                    type: number
                    example: 20
            additionalProperties: false

        AttendanceMetricsResponse:
            type: object
            required: [attendance_vs_all_registrations, attendance_vs_confirmed_registrations]
            properties:
                attendance_vs_all_registrations:
                    type: number
                    description: Percent (0–100) of checked-in attendees vs all registrations created this month
                attendance_vs_confirmed_registrations:
                    type: number
                    description: Percent (0–100) of checked-in attendees vs confirmed registrations created this month
            additionalProperties: false

        EventActivityMetricsResponse:
            type: object
            required: [registrations, tickets]
            properties:
                registrations:
                    type: array
                    items:
                        $ref: '#/components/schemas/GraphItem'
                tickets:
                    type: array
                    items:
                        $ref: '#/components/schemas/GraphItem'
            additionalProperties: false

        MemberGraphMetricsResponse:
            type: object
            required: [data]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/GraphItem'
            additionalProperties: false

        GraphItem:
            type: object
            required: [label, value]
            properties:
                label:
                    type: string
                    description: Date label
                value:
                    type: integer
                    minimum: 0

        GetEventMetricsResponse:
            type: object
            required:
                [
                    total_received,
                    payments_total,
                    previous_balance_in_kobo,
                    total_revenue_in_kobo,
                    total_registrations,
                    total_tickets,
                    total_attendees,
                    total_no_shows,
                    registrations,
                    reg_to_payment_conversion,
                    payment_to_attendance_conversion,
                    average_time_to_payment,
                    payments,
                    expenses,
                ]
            properties:
                total_received:
                    type: integer
                    description: Backwards-compatible alias of payments_total (kobo)
                payments_total:
                    type: integer
                    description: Sum of successful payments only (kobo)
                previous_balance_in_kobo:
                    type: integer
                    description: Event opening balance adjustment (kobo)
                total_revenue_in_kobo:
                    type: integer
                    description: payments_total + previous_balance_in_kobo (kobo)
                total_registrations:
                    type: integer
                total_tickets:
                    type: integer
                total_attendees:
                    type: integer
                total_no_shows:
                    type: integer
                registrations:
                    type: array
                    description: Per-day (YYYY-MM-DD) or per-month (YYYY-MM) new registration counts from event created through event day; daily if that span is under 30 days, else monthly
                    items:
                        $ref: '#/components/schemas/GraphItem'
                payments:
                    type: array
                    description: Per-day (YYYY-MM-DD) or per-month (YYYY-MM) cumulative successful payment amount in kobo (running total) from event created through event day; daily if that span is under 30 days, else monthly
                    items:
                        $ref: '#/components/schemas/GraphItem'
                expenses:
                    type: array
                    description: Per-day (YYYY-MM-DD) or per-month (YYYY-MM) cumulative NGN expense amount in kobo (running total) from event created through event day using expense_date; daily if that span is under 30 days, else monthly
                    items:
                        $ref: '#/components/schemas/GraphItem'
                reg_to_payment_conversion:
                    type: number
                    description: Percent of registrations that completed payment, 0–100
                payment_to_attendance_conversion:
                    type: number
                    description: Percent of sold tickets (confirmed payments) where the attendee checked in, 0–100
                average_time_to_payment:
                    type: number
                    description: Mean minutes from initial registration to payment confirmation (confirmed registrations only)

        UpdateEventFinanceAdjustmentRequest:
            type: object
            required: [previous_balance_in_kobo]
            properties:
                previous_balance_in_kobo:
                    type: integer
                    minimum: 0
            additionalProperties: false

        UpdateEventFinanceAdjustmentResponse:
            type: object
            required: [previous_balance_in_kobo]
            properties:
                previous_balance_in_kobo:
                    type: integer
                    minimum: 0
            additionalProperties: false

        JobsListResponse:
            type: object
            required: [data, meta]
            properties:
                data:
                    type: array
                    items:
                        $ref: '#/components/schemas/GetJobsResponseItem'
                meta:
                    $ref: '#/components/schemas/ListMeta'
            additionalProperties: false

        GetJobsResponseItem:
            type: object
            required: [id, name, payload, status, attempts, max_attempts, run_at, created_at]
            properties:
                id:
                    $ref: '#/components/schemas/UUID'
                name:
                    type: string
                payload:
                    type: object
                    additionalProperties: true
                status:
                    type: string
                    enum: [pending, processing, completed, failed]
                attempts:
                    type: integer
                max_attempts:
                    type: integer
                run_at:
                    $ref: '#/components/schemas/Timestamp'
                last_error:
                    type: string
                    nullable: true
                created_at:
                    $ref: '#/components/schemas/Timestamp'
                processed_at:
                    $ref: '#/components/schemas/Timestamp'
                    nullable: true
            additionalProperties: false

        RescheduleJobRequest:
            type: object
            properties:
                run_at:
                    $ref: '#/components/schemas/Timestamp'
            additionalProperties: false
