openapi: 3.0.3

info:
  title: WCONLINE API
  version: "1.0.0"
  description: |
    The WCONLINE API provides a single read endpoint, `GET /api`, that exports
    appointments, cancellations, client report forms (CRFs), availability, and
    orphaned appointments for a given date — used by reporting and
    data-warehouse integrations.

    ## Base URL

    Every request is made against **your center's own WCONLINE domain**. The
    examples here use the demo center `https://demo.mywconline.com` — replace it
    with your own domain (e.g. `https://yourschool.mywconline.com` or your custom
    domain).

    ## Authentication

    These docs describe the **bearer token** method only. Authenticate with the
    **API key** configured under **Global System Settings → Area Tools → API
    Configuration**, sent as a bearer token:

    ```http
    Authorization: Bearer {api_key}
    ```

    ## IP allowlist

    If you configure one or more IP addresses on the API Configuration page,
    requests from any other address are rejected with `403`. Leaving the
    allowlist empty disables the IP restriction.

    ## Rate limiting

    The `GET /api` endpoint is limited to **300 requests per hour** per API key.
    Exceeding it returns `429 Too Many Requests`.

    ## Time zones

    All dates and times in responses are returned in your institution's time
    zone — the one configured under **Global System Settings → General
    Settings** — rather than in UTC. Times use a 12-hour format (e.g. `9:00 am`).

    ## Errors

    Errors return a JSON body with a `message`, and — for validation failures —
    an `errors` object keyed by field name:

    ```json
    {
      "message": "The given data was invalid.",
      "errors": { "type": ["The selected type is invalid."] }
    }
    ```

servers:
  - url: https://demo.mywconline.com
    description: Demo center (replace with your own WCONLINE domain)

security:
  - bearerAuth: []

tags:
  - name: Data Export
    description: |
      Date-scoped export of appointments, cancellations, report forms, and
      availability.

paths:
  /api:
    get:
      tags:
        - Data Export
      summary: Export data for a single date
      operationId: exportData
      description: |
        Returns a JSON **array** of records for the requested `date`. The shape
        of each record depends on the `type` parameter:

        | `type`     | Each item is a… | Notes |
        |------------|-----------------|-------|
        | `APPT`     | `AppointmentRow` | Appointments on the date. |
        | `CANCELED` | `CanceledRow`    | Cancellations on the date (adds who/when canceled). |
        | `CRF`      | `CrfRow`         | Client report forms filed for the date. |
        | `ORPHAN`   | `AppointmentRow` | Appointments on the date that have **no** matching CRF. |
        | `AVAIL`    | `AvailabilityRow`| Open (available) appointment slots per visible schedule. |
        | `CUSTOM`   | `CustomRow`      | Tenant-defined export — **dynamic columns** (see schema). |

        All values are returned as **strings**. Boolean-style fields are either
        `"YES"` or an empty string `""`. When no records match, an empty array
        `[]` is returned.
      security:
        - bearerAuth: []
      parameters:
        - name: type
          in: query
          required: true
          description: The kind of records to export. Case-insensitive.
          schema:
            type: string
            enum: [APPT, AVAIL, CANCELED, CRF, CUSTOM, ORPHAN]
          example: APPT
        - name: date
          in: query
          required: true
          description: |
            The date to export, parsed leniently. `YYYYMMDD` and `YYYY-MM-DD`
            both work.
          schema:
            type: string
          example: "2025-04-01"
      responses:
        "200":
          description: |
            Matching records as a JSON array. Item shape depends on `type` (see
            the table above). An empty array means no records matched.
          content:
            application/json:
              schema:
                type: array
                items:
                  anyOf:
                    - $ref: "#/components/schemas/AppointmentRow"
                    - $ref: "#/components/schemas/CanceledRow"
                    - $ref: "#/components/schemas/CrfRow"
                    - $ref: "#/components/schemas/AvailabilityRow"
                    - $ref: "#/components/schemas/CustomRow"
              examples:
                appt:
                  summary: type=APPT
                  value:
                    - Appointment ID: "sc6f1a2b3c4d5e"
                      First Name: Jordan
                      Last Name: Rivera
                      Email Address: jrivera@example.edu
                      Schedule Title: Main Writing Center
                      Staff or Resource: Alex T.
                      Date: "2025-04-01"
                      Start Time: 9:00 am
                      End Time: 9:30 am
                      Walk-In: ""
                      No-Show: ""
                      Placeholder: ""
                      Online: ONLINE
                      Focus: Brainstorming
        "403":
          description: |
            Authentication failed (missing or invalid bearer token) or the
            caller's IP is not on the configured allowlist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                message: The API key provided does not match the one configured.
        "404":
          description: No API key is configured for this center.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                message: No API key configured.
        "422":
          description: The request parameters failed validation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationError"
              example:
                message: The type field is required.
                errors:
                  type:
                    - The type field is required.
        "429":
          description: Rate limit exceeded (300 requests per hour per API key).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                message: You have been rate limited for making too many requests.

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Your center's API key, configured under **Global System Settings →
        Area Tools → API Configuration**.

  schemas:
    AppointmentRow:
      type: object
      description: |
        A single appointment. Returned for `type=APPT` and `type=ORPHAN`
        (orphans are appointments with no matching CRF).
      properties:
        Appointment ID:
          type: string
          example: "sc6f1a2b3c4d5e"
        First Name:
          type: string
          description: |
            Header reflects your center's "first name" field label, which may be
            renamed. Defaults to **First Name**.
          example: Jordan
        Last Name:
          type: string
          description: |
            Header reflects your center's "last name" field label, which may be
            renamed. Defaults to **Last Name**.
          example: Rivera
        Email Address:
          type: string
          example: jrivera@example.edu
        Schedule Title:
          type: string
          example: Main Writing Center
        Staff or Resource:
          type: string
          example: Alex T.
        Date:
          type: string
          example: "2025-04-01"
        Start Time:
          type: string
          example: 9:00 am
        End Time:
          type: string
          example: 9:30 am
        Walk-In:
          type: string
          description: '`"YES"` for walk-in/drop-in appointments, otherwise `""`.'
          example: ""
        No-Show:
          type: string
          description: '`"YES"` if the appointment was missed, otherwise `""`.'
          example: ""
        Placeholder:
          type: string
          description: '`"YES"` for placeholder appointments, otherwise `""`.'
          example: ""
        Online:
          type: string
          description: '`"ONLINE"`, `"ETUTORING"`, or `""`.'
          example: ONLINE
        Focus:
          type: string
          description: The appointment focus, or `""` if none.
          example: Brainstorming

    CanceledRow:
      type: object
      description: A cancellation. Returned for `type=CANCELED`.
      properties:
        Appointment ID:
          type: string
          example: "sc6f1a2b3c4d5e"
        First Name:
          type: string
          example: Jordan
        Last Name:
          type: string
          example: Rivera
        Email Address:
          type: string
          example: jrivera@example.edu
        Schedule Title:
          type: string
          example: Main Writing Center
        Staff or Resource:
          type: string
          example: Alex T.
        Date:
          type: string
          example: "2025-04-01"
        Start Time:
          type: string
          example: 9:00 am
        End Time:
          type: string
          example: 9:30 am
        Walk-In:
          type: string
          example: ""
        No-Show:
          type: string
          example: ""
        Placeholder:
          type: string
          example: ""
        Online:
          type: string
          example: ""
        Focus:
          type: string
          example: ""
        Canceled By:
          type: string
          description: The name of the person who canceled, or `""`.
          example: Jordan Rivera
        Canceled Date:
          type: string
          description: When the cancellation happened (center timezone), or `""`.
          example: 2025-03-30 4:15:02 pm

    CrfRow:
      type: object
      description: A client report form (CRF). Returned for `type=CRF`.
      properties:
        Report ID:
          type: string
          example: "84213"
        Appointment ID:
          type: string
          description: |
            The originating appointment ID when the report is tied to a
            scheduled appointment, otherwise `""`.
          example: "sc6f1a2b3c4d5e"
        First Name:
          type: string
          example: Jordan
        Last Name:
          type: string
          example: Rivera
        Email Address:
          type: string
          example: jrivera@example.edu
        Schedule Title:
          type: string
          example: Main Writing Center
        Staff or Resource:
          type: string
          example: Alex T.
        Date:
          type: string
          example: "2025-04-01"
        Start Time:
          type: string
          example: 9:00 am
        End Time:
          type: string
          example: 9:30 am
        Length:
          type: string
          description: Appointment length in minutes.
          example: "30"
        Off-Schedule?:
          type: string
          description: '`"YES"` for an off-schedule report, otherwise `""`.'
          example: ""

    AvailabilityRow:
      type: object
      description: An open appointment slot. Returned for `type=AVAIL`.
      properties:
        Schedule Title:
          type: string
          example: Main Writing Center
        Date:
          type: string
          example: "2025-04-01"
        Start Time:
          type: string
          example: 9:00 am
        Resource:
          type: string
          description: The staff member or resource the slot is open on.
          example: Alex T.

    CustomRow:
      type: object
      description: |
        Returned for `type=CUSTOM`. **The columns are dynamic** — they are
        defined by your center's custom-export configuration and reflect your
        renamed field labels, custom form questions, and language settings.
        Because of this, no fixed schema can be published; every value is a
        string and the keys are whatever your custom export selects (e.g.
        `"Member ID"`, `"Email Address"`, the text of a registration question,
        `"Appointment Date"`, `"Last Login"`).
      additionalProperties:
        type: string
      example:
        Member ID: "1042"
        Email Address: jrivera@example.edu
        Standing: Sophomore
        Appointment Date: "2025-04-01"

    Error:
      type: object
      properties:
        message:
          type: string
      required: [message]

    ValidationError:
      type: object
      properties:
        message:
          type: string
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: Field name → list of validation messages.
      required: [message, errors]
