openapi: 3.0.3
info:
  title: Online Scout Manager API (Unofficial)
  version: 0.4.0
  description: |
    Unofficial, community-driven OpenAPI specification for the
    [Online Scout Manager](https://www.onlinescoutmanager.co.uk) (OSM) API —
    reverse-engineered from real traffic by the City of Newcastle Scouts Digital Team.

    OSM does not publish a public API specification. This project exists to fill
    that gap for developers building tools on top of OSM's platform.

    > **This documentation is provided as-is with no guarantees.** OSM's API is
    > undocumented and can change without notice. Endpoints, parameters, and
    > response shapes described here were observed at the time of writing and may
    > drift over time. Always test against the live API before relying on
    > anything here.

    ## Authentication

    OSM uses **OAuth 2.0 (Authorization Code)** for API access. To get started:

    1. Log in to OSM and navigate to **Settings → My Account Details → Developer Tools**.
    2. Create an OAuth application to obtain your `client_id` and `client_secret`.
    3. Implement the standard OAuth 2.0 authorization code flow — redirect users to
       OSM's authorization endpoint, exchange the code for tokens via `/oauth/token`.
    4. Include the access token as a **Bearer token** in the `Authorization` header
       on every API request.
    5. Tokens expire — use the refresh token flow to obtain new access tokens.

    ## Using the API

    Your browser uses a simple API for interacting with OSM, so although the API
    is unsupported and undocumented, your application can do anything that you can
    do on the website.

    Use your browser's developer console to watch the network requests made when
    you perform the actions you want to automate. Pay particular attention to
    whether requests are **GET** or **POST**. Then use your OAuth client library
    to create an authenticated request to the URL you discovered (which sets a
    Bearer token in the Authorization header).

    **Important:** All POST requests must use `application/x-www-form-urlencoded`
    encoding, **not** JSON. Sending JSON bodies will result in your application
    being blocked.

    ## Rate limits

    Please monitor the standard rate limit headers to ensure your application
    does not get blocked automatically. Applications that are frequently blocked
    will be **permanently blocked**.

    | Header | Description |
    |--------|-------------|
    | `X-RateLimit-Limit` | Number of requests per hour your API can perform (per authenticated user) |
    | `X-RateLimit-Remaining` | Requests remaining before the current user is blocked |
    | `X-RateLimit-Reset` | Seconds until the rate limit resets back to the overall limit |

    An **HTTP 429** status code is returned if the user exceeds the limit, along
    with a `Retry-After` header indicating the number of seconds to wait.

    Please also enforce your own lower rate limits, especially if you allow
    unauthenticated users to manipulate your data (e.g. allowing members to join
    a waiting list).

    ## Other considerations

    - **Check responses** — abort if they are not as expected. Your application
      will be blocked if it frequently performs invalid requests.
    - **`X-Deprecated` header** — anything with this header will be removed after
      the date mentioned. Monitor for it.
    - **`X-Blocked` header** — if set, your application has been blocked due to
      invalid data or unauthorized access attempts. Continuing to use the API
      after being blocked will automatically escalate to a **permanent block**.
    - **Bot protection** — if you allow unauthenticated use of your application,
      verify users are not bots.
    - **Input validation** — always sanitise and validate data before sending it
      to OSM. Invalid data will result in your application being blocked.
    - **Privacy and GDPR** — these endpoints return personal data about young
      people and their families. Handle it responsibly. Don't store what you
      don't need, don't log PII, and follow your organisation's data protection
      policies.
servers:
  - url: https://www.onlinescoutmanager.co.uk
  - url: https://onlinescoutmanager.co.uk
security:
  - oauth2: [ ]
tags:
  - name: Startup
    description: Session bootstrap and general metadata.
  - name: Members
    description: Member lists, details, patrols, and census.
  - name: Attendance
    description: Member attendance and badge requirements.
  - name: Custom Data
    description: Custom data groups and updates.
  - name: Email
    description: Member contact email selection and templates.
  - name: Programme
    description: Programme summaries, meetings, and attachments.
  - name: Badges
    description: Badge-related programme metadata.
  - name: Dashboard
    description: Dashboard summaries and next things.
  - name: Risk Assessments
    description: Programme and meeting risk assessments.
  - name: Events
    description: Events, event attendance and payments, Parent Portal settings, and kit lists.
  - name: Finance
    description: |
      Accounting (cashbook, bank accounts, budgets, reports), expense cards,
      and invoices.

      **Units:** the `/v3/finances/accounting/` endpoints use **integer pence**
      (`9706` = £97.06). Event endpoints (`/v3/events/…`, `/ext/events/…`)
      use **pounds**.
  - name: Files
    description: |
      Several features share a common file-attachment API. Each exposes a
      `manifest` action (sometimes namespaced, e.g. `emailerAttachmentsManifest`
      or `programmeAttachmentsManifest`) on its own URL, taking `id`, `path`
      (usually `/`), `section_id`, `temp`, plus a feature-specific ID.

      The response's `data.config.endpoints` tells you the `action` names to
      use on the same URL for each file operation (`put`, `get`, `preview`,
      `rename`, `delete`, `putTemp`/`commitTemp`/`revertTemp` for staged
      uploads, `partPut` for chunked uploads); an operation is `false` where
      it is not allowed. `config` also gives allowed extensions/MIME types and
      size limits, and `meta.size` the remaining quota.
  - name: Generic
    description: Cross-cutting helpers (edit locks, telemetry).
  - name: User
    description: Session keep-alive, per-user preferences, and notifications.
paths:
  /ext/generic/startup/:
    get:
      summary: Get startup data
      tags:
        - Startup
      description: |
        Returns startup and session data depending on the `action` parameter.

        **`action=getData` has been permanently removed** — this action was removed without
        notice. HTTP 410 Gone is the only response you will ever receive for this action.
        It will not be reinstated. Migrate to `action=getDataPayload`.

        **`action=getDataPayload`** returns a JSON payload containing CDN resource URLs,
        user globals, section roles and permissions, user preferences, terms, and session
        metadata needed to bootstrap the application after login.

        **`action=getConfigPayload`** returns global, non-user-specific site configuration:
        section type definitions, patrol role labels, beta features, upgrade pricing, feature
        flags, recipe metadata, and form configurations.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [getData, getDataPayload, getConfigPayload]
          description: |
            `getData` — **permanently removed**, HTTP 410 Gone is the only reply.
            `getDataPayload` — returns the full JSON startup payload.
            `getConfigPayload` — returns global site configuration.
      responses:
        "200":
          description: JSON startup payload (schema varies by `action`; see examples).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/StartupDataPayload"
                  - $ref: "#/components/schemas/StartupConfigPayload"
              examples:
                getDataPayload:
                  summary: Response for action=getDataPayload
                  value:
                    status: true
                    error: null
                    data:
                      cdn: "https://oymcdn.co.uk/"
                      resources:
                        - "/built/postlogin-combined/postlogin-combined-abc123.js"
                        - "/built/global/global-abc123.js"
                        - "/built/style-main-blue/style-main-blue-abc123.css"
                        - "/built/highcharts/highcharts-abc123.js"
                        - "/built/vendors/vendors-abc123.js"
                        - "/built/runtime/runtime-abc123.js"
                        - "/built/menu/menu-abc123.js"
                      config_hash: "abc123def456abc123def456abc123de"
                      required_cdn_built_files:
                        osm.js: "https://oymcdn.co.uk/built/osm/osm-abc123.js"
                        react.js: "https://oymcdn.co.uk/built/react/react-abc123.js"
                      cache_bust: "v=abc123"
                      globals:
                        post_login_action: false
                        theme: "blue"
                        userid: 100001
                        roles:
                          - groupname: "Example Scout Group"
                            groupid: "10001"
                            group_country: "uk"
                            sectionid: "45001"
                            sectionname: "Beavers"
                            section: "beavers"
                            isDefault: "0"
                            permissions:
                              badge: 20
                              member: 20
                              user: 100
                              register: 20
                              programme: 20
                              events: 20
                              flexi: 20
                              finance: 100
                              quartermaster: 20
                            sectionConfig:
                              subscription_level: 3
                              subscription_expires: "2027-01-01"
                              section_type: "beavers"
                              sectionType: "beavers"
                              parentSectionId: "0"
                              subscription_active: false
                              meeting_day: "Mon"
                              hierarchy_lowest_level: false
                              hierarchy_operating_level: "level_0"
                          - groupname: "Example Scout Group"
                            groupid: "10001"
                            group_country: "uk"
                            sectionid: "45002"
                            sectionname: "Cubs"
                            section: "cubs"
                            isDefault: "1"
                            permissions:
                              badge: 20
                              member: 20
                              user: "100"
                              register: 20
                              programme: 20
                              events: 20
                              flexi: 20
                              quartermaster: 20
                              finance: 100
                            sectionConfig:
                              subscription_level: 3
                              subscription_expires: "2027-01-01"
                              section_type: "cubs"
                              sectionType: "cubs"
                              parentSectionId: "0"
                              subscription_active: false
                              meeting_day: "Wed"
                              hierarchy_lowest_level: false
                              hierarchy_operating_level: "level_0"
                          - groupname: "Example Scout Group"
                            groupid: "10001"
                            group_country: "uk"
                            sectionid: "45003"
                            sectionname: "Leaders"
                            section: "adults"
                            isDefault: "0"
                            permissions:
                              badge: 100
                              member: 100
                              user: 100
                              register: 100
                              contact: 100
                              programme: 100
                              events: 100
                              finance: 100
                              flexi: 100
                              quartermaster: 100
                            sectionConfig:
                              subscription_level: 1
                              subscription_expires: "2027-01-01"
                              section_type: "adults"
                              sectionType: "adults"
                              parentSectionId: "0"
                              subscription_active: false
                              hierarchy_lowest_level: false
                              hierarchy_operating_level: "level_0"
                        active_user_member: false
                        associated_member_id: false
                        email: "leader@example.org"
                        firstname: "Alex"
                        lastname: "Scout"
                        fullname: "Alex Scout"
                        preferences:
                          logins: "100"
                          theme: "blue"
                          date: "uk"
                          sort: "dob"
                          has_assistive_technology: false
                          hotkeys_enabled: false
                          disable_animations: false
                          beta: false
                          beta_functions: []
                        member_access:
                          "45002":
                            name: "Example Scout Group: Cubs"
                            group: "Example Scout Group"
                            section: "Cubs"
                            abbrv: "Cubs"
                            type: "cubs"
                            label: "Cubs"
                            members:
                              "2100001":
                                first_name: "Alex"
                                last_name: "Scout"
                                photo_guid: "00000000-0000-0000-0000-000000000000"
                                abbrv_name: "Alex"
                                chat_rooms: []
                                access_type: "member"
                                has_unread_chat_rooms: false
                                permissions: true
                                patrol: "Leaders"
                            access: true
                            config:
                              badges: true
                              programme: true
                              emailbolton: true
                              details: true
                              events: true
                              chat: false
                              at_home: false
                              email_address: "Example Scout Group Cubs <cubs@example.org>"
                              giftaid: true
                              payments: true
                              attachments: false
                              photos: true
                            noticeboard: false
                            patrol_label: "Six"
                        member_access_unactivated: false
                        terms:
                          "45002":
                            - termid: "200001"
                              sectionid: "45002"
                              name: "Summer 2025"
                              startdate: "2025-04-01"
                              enddate: "2025-07-31"
                              master_term: null
                              past: true
                            - termid: "200002"
                              sectionid: "45002"
                              name: "Autumn 2025"
                              startdate: "2025-09-01"
                              enddate: "2025-12-31"
                              master_term: null
                              past: false
                        global_roles: []
                        hasSectionAccess: true
                        notepads: []
                        sockets: "45001-abc123,45002-abc123,45003-abc123"
                        user_socket: "100001-abc123"
                        session_id: "abc123def456abc123def456abc123def456abc123def456abc123def456abc1"
                        security_prompt: false
                        user_has_image: "/sites/onlinescoutmanager.co.uk/public/user_photos/100000/100001/00000000-0000-0000-0000-000000000000"
                        user_hash: "abc123def456abc123def456abc123def456abc123def456abc123def456abc1"
                        user_hash_legacy: "abc123def456abc123def456abc123def456abc123def456abc123def456abc1"
                        language: "en-GB"
                        gamification:
                          "1":
                            total_steps: 3
                            steps_completed: 2
                            id: 1
                          events:
                            total_steps: 10
                            steps_completed: 7
                            id: 2
                        gamification_rank: 500
                        gamification_total: 100000
                        features_used:
                          - "accessed"
                          - "viewed events"
                          - "edited attendance register"
                          - "sent email"
                        unread_message_count: 0
                        client_time: "1700000000000"
                      dojo:
                        cacheBust: "v=abc123"
                        locale: "en-gb"
                    meta: []
                getConfigPayload:
                  summary: Response for action=getConfigPayload
                  value:
                    status: true
                    error: null
                    data:
                      site_type: "scouts"
                      site_folder: "sites/onlinescoutmanager.co.uk/site"
                      siteConfig:
                        disableParentRota: false
                        expense_cards:
                          fees:
                            card_issuing: 10
                            lost_dispute: 20
                            fx_percentage: 3
                            cross_border:
                              percentage: 2
                              fixed: 0.3
                          maximum_overdrawn_charges: 10
                        online_payment_gateways:
                          - "gocardless"
                          - "stripe"
                        beta:
                          - key: "recipes"
                            title: "Recipes & Event Menus"
                            description: "Community driven recipe database with instructions for young people."
                            role: "all"
                            release_ts: 1638205905
                          - key: "waiting_list_invites"
                            title: "Waiting List Invites"
                            description: "Adds survey and invitation features to waiting list Personal Details."
                            role: "all"
                            release_ts: 1772443731
                            feedback:
                              min_usage: 1
                              mode: "new"
                              feature: "waiting list survey or invitation"
                          - key: "scheduled_emails"
                            title: "Scheduled Emails"
                            description: "Adds a Send later option when composing emails to members."
                            role: "all"
                            release_ts: 1779355830
                            feedback:
                              min_usage: 3
                              mode: "new"
                              feature: "scheduled email"
                          - key: "back_button"
                            title: "Browser's Back Button"
                            description: "Browser back button now closes dialogs and navigates between tabs."
                            role: "all"
                            release_ts: 1782137873
                            feedback:
                              min_days: 14
                              mode: "new"
                        boltons:
                          b: "badges"
                          c: "campsiteexternalbookings"
                          d: "details"
                          e: "events"
                          m: "emailbolton"
                          p: "programme"
                          a: "accounts"
                          f: "filestorage"
                          r: "chat"
                          i: "ai"
                          t: "tasks"
                          h: "at_home"
                        hasOnlinePayments: true
                        upgrade_config:
                          badges:
                            price: 1
                          events:
                            price: 10
                          ai:
                            price: 7.5
                          programme:
                            price: 1
                          details:
                            price: 2.5
                          emailbolton:
                            price: 4.5
                          filestorage:
                            price: 5
                          silver:
                            price: 18
                          gold:
                            price: 24
                          campsiteexternalbookings:
                            price: 36
                          chat:
                            price: 2.5
                          at_home:
                            price: 10
                          accounts:
                            price: 20
                          tasks:
                            price: 2.5
                        sms_prices:
                          "250": 14.25
                          "500": 28
                          "1000": 55
                        hideSmsOriginator: false
                        filestorage_bolton: true
                        chat_bolton: true
                        whatsnew: true
                        risk_assessments: true
                        recipes:
                          meals: ["breakfast", "snack", "lunch", "dinner", "pudding", "drink"]
                          methods: ["stove", "oven", "fire", "dutch", "expedition", "no-cooking"]
                          suitability: ["vegetarian", "vegan", "gluten", "dairy", "nuts", "wheat", "fish", "shellfish", "egg", "oatmeal", "soya"]
                          allergies: ["meat", "animal", "gluten", "dairy", "nuts", "wheat", "fish", "shellfish", "egg", "oatmeal", "soya"]
                          aisles: ["dairy", "deli", "bakery", "vegetable", "fruit", "fish", "frozen", "meat", "canned", "condiments", "dried", "drink", "snack", "simple", "cereal", "cleaning", "other"]
                        hideMyPermissions: false
                        events_attachments_quota: 50
                        events_attachments_quota_private: -1
                        user_can_delete_section: true
                        add_invoices_to_events: false
                        hide_user_active_subscriptions: false
                        user_can_rename_self: true
                        members_discoverable: false
                        event_approvals_config:
                          assign: false
                        events_force_allow_changes: "configurable"
                        events_prevent_deadline_change: false
                        hide_age_after: 25
                        hq_terms: false
                        default_currency: "GBP"
                        ask_communication_preferences: false
                        email_list_domain: "lists.onlinescoutmanager.co.uk"
                        waiting_list_application: ["contact_primary_1", "contact_primary_2"]
                        myscout: "Parent Portal"
                        communicatable_emails_use_opt_ins: []
                        allowFutureJoinedMovement: false
                        joinedMovementOptional: false
                        hideExternalAccess: false
                        support_network: false
                        support_network_personal: false
                        support_network_disabled_section_types: []
                        event_shared_member_record_cards: false
                        language: "en-GB"
                        available_languages:
                          en-GB: "English (British)"
                        disable_patrol_points: false
                        disable_help_videos: false
                        file_proxy: "https://files.onlineyouthmanager.co.uk/"
                        cdn: "oymcdn.co.uk"
                        global_cdn: "oymcdn.co.uk"
                        hide_upgrades: false
                        root_url: "https://www.onlinescoutmanager.co.uk"
                        url_www: "www.onlinescoutmanager.co.uk"
                        fullname: "Online Scout Manager"
                        myscout_email: "myscout@onlinescoutmanager.co.uk"
                        section_types:
                          uk:
                            earlyyears: "Squirrels"
                            beavers: "Beavers"
                            cubs: "Cubs"
                            scouts: "Scouts"
                            explorers: "Explorers"
                            network: "Network"
                            adults: "Adults"
                          navigators:
                            juniornavigators: "Juniors"
                            seniornavigators: "Seniors"
                          au:
                            ausscouts: "Scouts"
                            aucubs: "Cubs"
                            aujoeys: "Joeys"
                        badge_types:
                          uk: ["challenge", "staged", "core", "activity"]
                          au: ["activity", "challenge"]
                        adult_section_types: ["adults"]
                        adult_section_types_emailable: ["adults"]
                        youth_section_types: ["earlyyears", "beavers", "cubs", "scouts", "explorers", "network", "juniornavigators", "seniornavigators", "aujoeys", "aucubs", "ausscouts", "campsite"]
                        free_sections: ["adults", "waiting", "campsite", "national"]
                        abbr: "osm"
                        facebookpage: "onlinescoutmanager"
                        personaldetails_configurable: true
                        colour_primary: "4d2177"
                        colour_secondary: "84a40b"
                        colour_tertiary: "8b0066"
                        default_currency_symbol: "£"
                        communicatable_contact_groups:
                          default:
                            contact_primary_1: "Primary Contact 1"
                            contact_primary_2: "Primary Contact 2"
                            contact_primary_member: "Member"
                          campsite:
                            contact_primary_campsite: "Customer"
                        section_type_setup:
                          beavers:
                            ai_risk_activities: true
                            programme: true
                            events: true
                            flexi: true
                            quartermaster: true
                            attendance: true
                            finances: true
                            giftaid: true
                            patrols: true
                            subscription: true
                            badgeMenu: true
                            isScout: true
                            showStartedSection: true
                            isDOBNeeded: true
                            isWaiting: false
                            isAdult: false
                            useTerms: true
                            showDetails: true
                            campDirectory: true
                            census: true
                            allowSMS: true
                            showCountry: true
                            myscout: true
                            onlinepayments: true
                            waitingListType: "waiting"
                            badgeTitle: "Badges"
                            showSectionSizePredictor: true
                            showBadgesByPerson: true
                            sectionSizePredictor:
                              earlyyears: 2190
                              beavers: 2920
                              cubs: 3832
                              scouts: 5110
                              explorers: 6570
                              network: 9125
                            section_colours_ages:
                              earlyyears: ["#ca1a10", 4, 6]
                              beavers: ["#0099FF", 6, 8]
                              cubs: ["#33CC00", 8, 10.5]
                              scouts: ["#006666", 10.5, 14]
                              explorers: ["#999966", 14, 18]
                              network: ["#4d2177", 18, 25]
                              waiting: ["#999999", -1, -1]
                            fileStorage: true
                            badgeStock: true
                            badges:
                              challenge:
                                type: "challenge"
                                singular: "Challenge"
                                menu: "Challenge"
                                title: "Challenge Badges"
                                type_id: 1
                              activity:
                                type: "activity"
                                singular: "Activity"
                                menu: "Activity"
                                title: "Activity Badges"
                                type_id: 2
                              staged:
                                type: "staged"
                                singular: "Staged"
                                menu: "Staged"
                                title: "Staged Badges"
                                type_id: 3
                              core:
                                type: "core"
                                singular: "Core"
                                menu: "Core"
                                title: "Core Badges"
                                type_id: 4
                            audit_trail:
                              member:
                                note_types: ["notes", "received-email", "received-phonecall", "received-letter", "sent-email", "sent-phonecall", "sent-letter"]
                              section:
                                note_types: ["notes"]
                            showParentPortalLoginHistory: true
                            hasYoungLeadersPatrol: true
                            hideUpgrades: false
                          adults:
                            programme: true
                            events: true
                            flexi: true
                            quartermaster: true
                            giftaid: true
                            attendance: false
                            finances: true
                            badgeTitle: "Records"
                            subscription: false
                            badgeMenu: false
                            isDOBNeeded: false
                            isScout: false
                            showStartedSection: true
                            isAdult: true
                            myscout: true
                            patrols: true
                            isWaiting: false
                            showDetails: true
                            useTerms: true
                            waitingListType: "waiting"
                            allowSMS: true
                            showYouthBadgeLinks: false
                            showCountry: true
                            census: true
                            onlinepayments: true
                            fileStorage: true
                            badges:
                              challenge:
                                type: "challenge"
                                singular: "Records"
                                menu: "Records"
                                title: "Records"
                                type_id: 1
                            audit_trail:
                              member:
                                note_types: ["notes", "received-email", "received-phonecall", "received-letter", "sent-email", "sent-phonecall", "sent-letter"]
                              section:
                                note_types: ["notes"]
                            showParentPortalLoginHistory: true
                            excludeFromHierarchy: true
                            hideUpgrades: false
                          waiting:
                            programme: false
                            events: false
                            flexi: true
                            attendance: false
                            patrols: false
                            finances: false
                            subscription: false
                            badgeMenu: false
                            isScout: false
                            showStartedSection: false
                            isDOBNeeded: true
                            showSectionSizePredictor: true
                            isAdult: false
                            isWaiting: true
                            showDetails: true
                            useTerms: false
                            allowSMS: true
                            waitingListType: "waiting"
                            showYouthBadgeLinks: false
                            badges: []
                            audit_trail:
                              member:
                                note_types: ["notes", "received-email", "received-phonecall", "received-letter", "sent-email", "sent-phonecall", "sent-letter"]
                              section:
                                note_types: ["notes"]
                            showParentPortalLoginHistory: true
                            excludeFromHierarchy: true
                            hideUpgrades: false
                          discount:
                            programme: false
                            events: false
                            flexi: false
                            attendance: false
                            finances: false
                            subscription: false
                            badgeMenu: false
                            isScout: false
                            showStartedSection: false
                            isDOBNeeded: false
                            isAdult: false
                            isWaiting: false
                            isDiscount: true
                            hideForum: true
                            useTerms: false
                            allowSMS: false
                            hideUpgrades: true
                            showDetails: false
                            badges: []
                            audit_trail:
                              member:
                                note_types: ["notes", "received-email", "received-phonecall", "received-letter", "sent-email", "sent-phonecall", "sent-letter"]
                              section:
                                note_types: ["notes"]
                            showParentPortalLoginHistory: false
                            excludeFromHierarchy: true
                            disableOffline: true
                        buy_links:
                          due_badge_bags: "https://example.com/due-badge-bags"
                        feature_preview: []
                        risk_assessments_config:
                          mode: "freeform"
                          reuse_programme: true
                          reuse_events: true
                          residual_risk: false
                          who: true
                          has_generic_events: true
                          has_activities: true
                        tld: "onlinescoutmanager.co.uk"
                        accounting: true
                        accounting_expense_cards: true
                        activity_database: true
                        ai: true
                        api_manage_hq: false
                        badge_homework: true
                        badge_show_completion_certificate: false
                        badge_wizard: true
                        campaign_template_designs: ["purpleandgreen", "lightblue", "darkblue", "turquoise", "purpleandpink", "purple", "default", "blank"]
                        deletable_users: true
                        duplicate_member_checks: false
                        elearning_editor: false
                        elearning_viewer: false
                        email_from_reports: []
                        email_signatures: true
                        email_template_colours: ["#ffffff", "#000000", "#ffcd00", "#ff8200", "#ba0c2f", "#7C3A2D", "#AD1AAC", "#7413dc", "#4d2177", "#003DA5", "#002855", "#0095C8", "#004851", "#00594F", "#00a794", "#009A44", "#84A40B", "#8C857B"]
                        event_directions: true
                        event_duplicate: true
                        event_types_single_select: false
                        events_additional_information: []
                        events_approvals: false
                        events_at_home: true
                        events_parent_rota: true
                        events_past_read_only: false
                        events_personal_details_confirmation_restriction: "standard_fields:confirmation:24267"
                        events_summary_approval_status_highlights: []
                        events_v2: true
                        group_badges: true
                        group_dashboard: true
                        hasJoinedMovement: true
                        has_main_section_per_person: false
                        help_websites: false
                        hierarchy_sms: false
                        hq_support: false
                        kill:
                          built_files: false
                          built_files_admin: false
                          cdn: false
                          cdn_admin: false
                          extralogging: false
                          extralogging_admin: false
                          gilbert: false
                          gilbert_admin: false
                          gilbert_bedrock: false
                          gilbert_bedrock_admin: false
                          gilbert_support: false
                          gilbert_support_admin: false
                          gocardless: false
                          gocardless_admin: false
                          intrusiondetection: false
                          intrusiondetection_admin: false
                          offlinedata: false
                          offlinedata_admin: false
                          proxysql: false
                          proxysql_admin: false
                          sms: false
                          sms_admin: false
                        kit_lists: true
                        leader_parent_portal_file_storage_without_upgrade: false
                        lockdown_email_from_address: false
                        member_deletion_review: true
                        member_elsewhere_review: false
                        member_erase_extra_data: ["badge", "events", "register", "finance"]
                        member_removal_warn_if_active: false
                        member_view_email_history: false
                        member_view_own_badge_details: false
                        members_users_linked: false
                        members_users_linked_edit: false
                        mymember_access_restrictions: "section"
                        national_opt_in_or_out: false
                        national_opt_in_or_out_sms: false
                        national_parent_portal_file_storage: false
                        organisation_import: false
                        parent_portal_auto_join: "opt-in"
                        parent_portal_date_of_birth_custom_data_group: "standard_fields"
                        parent_portal_thank_you_messages: true
                        prevent_import_spreadsheet: false
                        prevent_leader_edit_portal_templates: false
                        programme_primary_leader: true
                        programmes_at_home: true
                        purge_event_audit_trail: 3
                        remove_member_allowed_functions:
                          default: ["keepname", "keepcontact", "keepall", "delete", "erase"]
                        require_global_role_inactive_searches: false
                        schedule_emails: true
                        section_edit_details: false
                        section_edit_details_mode: "all_sections"
                        section_editable_locations: false
                        section_requires_two_admins: true
                        shared_events_opt_in_email: true
                        showCommentsOnAllDashboards: false
                        show_additional_roles_dates_comments: false
                        show_community_help_forum: true
                        show_feature_requests: true
                        summary_dashboards: false
                        to_do_tasks: true
                        urgent_event_approvals: -1
                      patrolLabels:
                        scouts:
                          patrol: "Patrol"
                          patrols: "Patrols"
                        cubs:
                          patrol: "Six"
                          patrols: "Sixes"
                        beavers:
                          patrol: "Lodge"
                          patrols: "Lodges"
                        explorers:
                          patrol: "Patrol"
                          patrols: "Patrols"
                        network:
                          patrol: "Patrol"
                          patrols: "Patrols"
                        adults:
                          patrol: "Section / Role"
                          patrols: "Sections"
                        earlyyears:
                          patrol: "Nest"
                          patrols: "Nests"
                        juniornavigators:
                          patrol: "Team"
                          patrols: "Teams"
                        seniornavigators:
                          patrol: "Team"
                          patrols: "Teams"
                        aucubs:
                          patrol: "Six"
                          patrols: "Sixes"
                      patrolRoles:
                        scouts:
                          - level: 0
                            label: "Normal"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 1
                            label: "Assistant Patrol Leader"
                            abbr: "APL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 2
                            label: "Patrol Leader"
                            abbr: "PL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 3
                            label: "Senior Patrol Leader"
                            abbr: "SPL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                        cubs:
                          - level: 0
                            label: "Normal"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 1
                            label: "Seconder"
                            abbr: "2nd"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 2
                            label: "Sixer"
                            abbr: "6er"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 3
                            label: "Senior Sixer"
                            abbr: "S6er"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                        beavers:
                          - level: 0
                            label: "Normal"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 1
                            label: "Assistant Lodge Leader"
                            abbr: "ALL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 2
                            label: "Lodge Leader"
                            abbr: "LL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 3
                            label: "Senior Lodge Leader"
                            abbr: "SLL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                        explorers:
                          - level: 0
                            label: "Normal"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 1
                            label: "Assistant Patrol Leader"
                            abbr: "APL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 2
                            label: "Patrol Leader"
                            abbr: "PL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          - level: 3
                            label: "Senior Patrol Leader"
                            abbr: "SPL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                        earlyyears:
                          - level: 0
                            label: "Normal"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                        adults:
                          "0":
                            level: 0
                            label: "Unspecified"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "1":
                            level: 1
                            label: "Section Team Member"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "2":
                            level: 2
                            label: "Section Team Leader"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "3":
                            level: 3
                            label: "Young Leader"
                            abbr: "YL"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "6":
                            level: 6
                            label: "Group Lead Volunteer"
                            abbr: "GLV"
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "7":
                            level: 7
                            label: "Chair"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "8":
                            level: 8
                            label: "Treasurer"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "9":
                            level: 9
                            label: "Secretary"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                          "15":
                            level: 15
                            label: "Trustee"
                            abbr: ""
                            changes: null
                            census_costs: false
                            visibility: 0
                            allow_duplicates: true
                            archived: 0
                      extraSectionTypes:
                        staged: "n/a"
                        adults: "Adults"
                        aucubs: "Australian Cubs"
                        zadventure: "Adventure"
                        campsite: "Campsite"
                        national: "National Office"
                      featurelevels:
                        addbadge: 3
                        badgeeligibility: 3
                        badge_records: 1
                        badgepdf: 3
                        badgespreadsheet: 3
                        calendarsync: 3
                        census: 3
                        eventdownload: 3
                        events: 2
                        eventsharing: 2
                        exportemail: 2
                        exportpatrolpdf: 3
                        extrapdf: 3
                        extras: 2
                        filestorage: 3
                        finances: 3
                        genericpdf: 3
                        giftaid: 3
                        helloworld: 99
                        ics: 3
                        intouchpdf: 2
                        mobile: 2
                        outstanding: 2
                        printoutstanding: 3
                        programmepdf: 3
                        quartermaster: 3
                        recordcard: 3
                        register: 2
                        registerpdf: 3
                        reporting: 1
                        sendemail: 2
                        sharebadgestock: 3
                        showprogrammeinbadgerecords: 3
                        spreadsheet: 3
                        survey: 3
                        recipes: 3
                        risk_assessments: 3
                        badgewizard: 3
                        activity: 2
                        badgestock: 1
                        programme: 1
                        accidentforms: 3
                      changedlevels: []
                      availableRoles:
                        nobody: "Nobody"
                        siteadministrator: "OSM Site Administrator"
                      globalAccessRoles:
                        - user_role: "siteadministrator"
                          access_type: "kill_switches"
                          id: "n/a"
                        - user_role: "_support"
                          access_type: "audit_member_support"
                          id: "*"
                        - user_role: "_support"
                          access_type: "grant_access"
                          id: "*"
                        - user_role: "_all"
                          access_type: "preview_badge_wizard"
                          id: "n/a"
                        - user_role: "badge_orders"
                          access_type: "online_badge_order"
                          id: "*"
                      terminology:
                        Level_1: "Group"
                        Level_2: "District"
                        Level_3: "County"
                        Level_4: "Region/Area"
                        Level_5: "Country"
                        Level_6: "Association"
                      forms:
                        - name: "Accident Forms"
                          menu_config:
                            - reference: "ext.members.health"
                              menu: "members"
                              area: "members"
                              offline: false
                              order: 10
                              subscription_feature: "accidentforms"
                              title: "Accident Forms"
                              title_is_translated: true
                              react: "ext_forms_FormListPane"
                        - name: "Medication History"
                          menu_config:
                            - reference: "ext.members.medication"
                              menu: "members"
                              area: "members"
                              offline: false
                              order: 11
                              subscription_feature: "accidentforms"
                              title: "Medication History"
                              title_is_translated: true
                              react: "ext_forms_FormListPane"
                        - name: "Incident Logs"
                          menu_config:
                            - reference: "ext.members.incidents"
                              menu: "members"
                              area: "members"
                              offline: false
                              order: 15
                              subscription_feature: "accidentforms"
                              title: "Incident Logs"
                              title_is_translated: true
                              react: "ext_forms_FormListPane"
                        - name: "Entrepreneur Fund"
                          menu_config:
                            - reference: "ext.forms.entrepreneur"
                              menu: "sectionadmin"
                              area: "grants"
                              offline: false
                              order: 1000
                              subscription_feature: "accidentforms"
                              title: "Entrepreneur Fund"
                              title_is_translated: true
                              react: "ext_forms_FormListPane"
                      default_terminology:
                        section: "section"
                        sections: "sections"
                        Section: "Section"
                        Sections: "Sections"
                        Group: "Group"
                        group: "group"
                        District: "District"
                        Level_1: "Group"
                        Level_2: "District"
                        Level_3: "Area"
                        Level_4: "Country"
                        Level_5: ""
                        Level_6: ""
                      leaderPatrolIds:
                        - -2
                        - -3
                    meta: []
        "410":
          description: |
            Gone — `action=getData` has been permanently removed. HTTP 410 Gone is
            the only reply you will ever receive for that action.
  /ext/members/contact/:
    get:
      summary: Get list of members or individual member details
      tags:
        - Members
      description: |
        Retrieves member data. The `action` parameter controls the behavior.
        This endpoint is also used to return full member contact/emergency data
        (use with care for GDPR compliance).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getListOfMembers, getIndividual, getMemberTransfers ]
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: false
          schema:
            type: string
          description: Term ID.
        - name: section
          in: query
          required: false
          schema:
            type: string
            enum: [ squirrels, beavers, cubs, scouts, explorers ]
          description: Section type.
        - name: sort
          in: query
          required: false
          schema:
            type: string
            enum: [ dob, firstname, lastname, patrol ]
          description: Sort order for list of members.
        - name: scoutid
          in: query
          required: false
          schema:
            type: string
          description: Member unique identifier.
        - name: context
          in: query
          required: false
          schema:
            type: string
            enum: [ members ]
          description: Context for member detail retrieval.
        - name: mode
          in: query
          required: false
          schema:
            type: string
          description: Display mode (observed value `grid`).
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID (alternate naming).
      responses:
        "200":
          description: Member data response.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/MembersList"
                  - $ref: "#/components/schemas/MemberDetail"
                  - $ref: "#/components/schemas/MemberTransfers"
                additionalProperties: true
              examples:
                membersList:
                  summary: Members list
                  value:
                    identifier: scoutid
                    photos: true
                    items:
                      - firstname: Jamie
                        lastname: River
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        patrolid: 12001
                        patrol: Blue
                        sectionid: 45001
                        enddate: null
                        age: "10 / 2"
                        patrol_role_level_label: Sixer
                        active: true
                        scoutid: 3100001
                        full_name: "Jamie River"
                        has_invitations: false
                memberDetail:
                  summary: Member detail
                  value:
                    ok: true
                    read_only: [ ]
                    data:
                      scoutid: "3100001"
                      firstname: Jamie
                      lastname: River
                      photo_guid: "00000000-0000-0000-0000-000000000000"
                      dob: "2015-04-22"
                      started: "2021-09-01"
                      created_date: "2021-09-01 18:30:00"
                      last_accessed: "2025-09-15 19:00:00"
                      patrolid: "12001"
                      patrolleader: "2"
                      startedsection: "2021-09-01"
                      enddate: null
                      age: "10 years and 4 months"
                      age_simple: "10 / 04"
                      sectionid: 45001
                      active: true
                      has_invitations: false
                      moving_on_date: "2025-12-19"
                      moving_on_section_name: "Scouts"
                    meta: [ ]
                memberTransfers:
                  summary: Member transfers
                  value:
                    status: true
                    error: null
                    data:
                      - direction: from
                        member_id: 3100002
                        firstname: Riley
                        lastname: Park
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        type: transferred
                        date: "2024-01-15"
                        section_id: 65001
                        section_name:
                          group: "1st Riverside Scouts"
                          section: Scouts
                        section_long: "1st Riverside Scouts: Scouts"
                        mode: outgoing
                        id: "outgoing-123456"
                    meta: [ ]
    post:
      summary: Update a member field or contact details
      tags:
        - Members
      description: |
        Updates a single field on a member record, or updates contact details
        for one of their linked contacts. The `action` query parameter must be
        `update`. The body is sent as `application/x-www-form-urlencoded`.

        **Member field mode** — send `scoutid`, `column`, `value`, `sectionid`,
        and optionally `context=members` (the web UI's patrol drag-and-drop
        omits `context` and still succeeds). Known columns:

        | Column          | Value format | Notes |
        |-----------------|-------------|-------|
        | `firstname`     | string | |
        | `lastname`      | string | |
        | `dob`           | `yyyy-mm-dd` | |
        | `started`       | `yyyy-mm-dd` | Date started Scouting. |
        | `startedsection`| `yyyy-mm-dd` | Date started this section. |
        | `patrolid`      | int | Must also provide `patrolleader` in the same request body. |
        | `patrolleader`  | int | 0 = no rank, 1 = APL/Seconder, 2 = PL/Sixer, 3 = SPL/Senior Sixer. |

        When setting `patrolid`, the body **must also include** `patrolleader`
        (the rank level integer). Conversely, `patrolleader` can be set on its
        own as `column` without changing the patrol.

        **Contact update mode** — send `associated_type=member`,
        `associated_id=<scoutid>`, `group_id=<contact_group>`, `context=members`,
        and `data[field]=value` pairs for each field. Contact group IDs:

        | group_id | Contact |
        |----------|---------|
        | 1 | Primary Contact 1 |
        | 2 | Primary Contact 2 |
        | 3 | Emergency Contact |
        | 4 | Doctor's Surgery |
        | 6 | Member's own details |
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ update ]
          description: Must be `update`.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
              properties:
                scoutid:
                  type: integer
                  description: Member unique identifier (member field mode).
                column:
                  type: string
                  enum: [ firstname, lastname, dob, started, startedsection, patrolid, patrolleader ]
                  description: The field to update (member field mode).
                value:
                  oneOf:
                    - type: integer
                    - type: string
                  description: The new value.
                sectionid:
                  type: string
                  description: Section ID the member belongs to.
                context:
                  type: string
                  enum: [ members ]
                  description: Must be `members`.
                patrolleader:
                  type: integer
                  description: |
                    Required when `column=patrolid`. Rank level:
                    0 = none, 1 = APL/Seconder, 2 = PL/Sixer, 3 = SPL/Senior Sixer.
                associated_type:
                  type: string
                  enum: [ member ]
                  description: Contact update mode — must be `member`.
                associated_id:
                  type: integer
                  description: Contact update mode — the member's scoutid.
                group_id:
                  type: integer
                  enum: [ 1, 2, 3, 4, 6 ]
                  description: Contact update mode — contact group ID.
            examples:
              memberFieldUpdate:
                summary: Update a member field (e.g. patrol)
                value:
                  scoutid: 3100001
                  column: patrolid
                  value: "12002"
                  sectionid: "45001"
                  context: members
                  patrolleader: 0
              contactUpdate:
                summary: Update a contact's details
                value:
                  associated_type: member
                  associated_id: "3100001"
                  group_id: "1"
                  context: members
                  "data[firstname]": "Morwenna"
                  "data[lastname]": "River"
                  "data[email1]": "morwenna.river@example.org"
                  "data[phone1]": "07700900123"
                  "data[phone1_sms]": "yes"
      responses:
        "200":
          description: Update confirmation with last-updated metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  meta:
                    type: object
                    properties:
                      last_updated:
                        type: string
                        description: Timestamp of the update (e.g. `2025-09-15 19:42:10`).
                      last_updated_by_name:
                        type: string
                        description: Name of the user who made the update.
                additionalProperties: true
              example:
                ok: true
                meta:
                  last_updated: "2025-09-15 19:42:10"
                  last_updated_by_name: "Alex Scout"
  /ext/members/contact/actions/:
    post:
      summary: Create, remove, or restore a member
      tags:
        - Members
      description: |
        Member lifecycle actions. The `action` query parameter controls the
        behaviour. Body is `application/x-www-form-urlencoded`.

        **`newMember`** — creates a new member in the section.

        **`removeMember`** — removes a member from the section. The `type`
        field controls how much data is retained:

        | type | Effect |
        |------|--------|
        | `keepall` | Keep all data intact |
        | `keepcontact` | Remove personal details except name and core details |
        | `keepname` | Remove personal details except name |
        | `delete` | Remove all personal details; use initials for names (member history retains names) |
        | `erase` | Remove all personal details **including** names from history. **Irreversible.** |

        When `type=erase`, an optional `additional_data_to_delete` array can
        specify extra data categories to purge: `badge`, `events`, `finance`,
        `register`. Each is optional.

        **`returnMember`** — restores a previously removed member.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ newMember, removeMember, returnMember ]
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID (required for `removeMember`).
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
              properties:
                firstname:
                  type: string
                  description: "`newMember` only. First name."
                lastname:
                  type: string
                  description: "`newMember` only. Last name."
                dob:
                  type: string
                  format: date
                  description: "`newMember` only. Date of birth (`yyyy-mm-dd`)."
                startedsection:
                  type: string
                  format: date
                  description: "`newMember` only. Date started in this section."
                started:
                  type: string
                  format: date
                  description: "`newMember` only. Date started Scouting."
                sectionid:
                  type: string
                  description: Section ID.
                originating_section_id:
                  type: string
                  description: "`newMember` only. Usually same as `sectionid`."
                term_id:
                  type: string
                  description: "`newMember` only. Term ID."
                scouts:
                  type: integer
                  description: "`removeMember` / `returnMember`. Member scout ID."
                from_section:
                  type: string
                  description: "`removeMember` only. Section ID to remove from."
                date:
                  type: string
                  format: date
                  description: "`removeMember` only. Effective removal date (`yyyy-mm-dd`)."
                type:
                  type: string
                  enum: [ keepall, keepcontact, keepname, delete, erase ]
                  description: "`removeMember` only. Data retention level."
                additional_data_to_delete:
                  type: array
                  items:
                    type: string
                    enum: [ badge, events, finance, register ]
                  description: "`removeMember` with `type=erase` only. Extra data to purge."
            examples:
              newMember:
                summary: Create a new member
                value:
                  firstname: Rowan
                  lastname: Wells
                  dob: "2016-06-15"
                  startedsection: "2025-09-05"
                  started: "2025-09-06"
                  sectionid: "45001"
                  originating_section_id: "45001"
                  term_id: "67890"
              removeMember:
                summary: Remove a member (keep all data)
                value:
                  scouts: 3100005
                  from_section: "45001"
                  date: "2025-08-19"
                  type: keepall
              returnMember:
                summary: Restore a removed member
                value:
                  scouts: 3100005
      responses:
        "200":
          description: Action result.
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    description: "`newMember` response."
                    properties:
                      result:
                        type: string
                        enum: [ ok ]
                      out_of_term:
                        type: boolean
                        description: Whether the start date falls outside the given term.
                      scoutid:
                        type: integer
                        description: The newly assigned member ID.
                      firstname:
                        type: string
                      lastname:
                        type: string
                  - type: object
                    description: "`removeMember` / `returnMember` response."
                    properties:
                      ok:
                        type: boolean
                      scouts:
                        type: array
                        items:
                          type: integer
                        description: Scout IDs affected.
              examples:
                newMember:
                  summary: New member created
                  value:
                    result: ok
                    out_of_term: false
                    scoutid: 3100005
                    firstname: Rowan
                    lastname: Wells
                removeMember:
                  summary: Member removed
                  value:
                    ok: true
                    scouts: [ 3100005 ]
                returnMember:
                  summary: Member restored
                  value:
                    ok: true
                    scouts: [ 3100005 ]
  /ext/badges/records/:
    get:
      summary: Get badge records or available badges
      tags:
        - Badges
      description: |
        Returns badge data. The `action` parameter controls the behavior:
        - `getBadgeRecords` — returns per-member completion records for a specific badge.
        - `getAvailableBadges` — returns the list of badges available for a section and type.
          When called with `asNumericArray=y&liveOnly=y&addGroupToName=y` (as the
          attendance register does), the response is a Dojo item store
          (`identifier` / `label` / `items`) containing every live badge for the
          section across all types, and `type_id` / `term_id` are not needed.
          Either `sectionid` or `section_id` is accepted.
        - `getColumnsForBadge` — returns the requirement columns (the individual
          criteria, grouped into lettered modules) for one badge version.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getBadgeRecords, getAvailableBadges, getColumnsForBadge ]
        - name: section
          in: query
          required: true
          schema:
            type: string
            enum: [ squirrels, beavers, cubs, scouts, explorers ]
          description: Section type.
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID (used with `getAvailableBadges`).
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID (alternate naming, used with some actions).
        - name: term_id
          in: query
          required: false
          schema:
            type: string
          description: Term ID (not sent by the item-store form of `getAvailableBadges`).
        - name: type_id
          in: query
          required: false
          schema:
            type: string
            enum: [ "1", "2", "3", "4" ]
          description: |
            Badge type ID.
            1 = Challenge, 2 = Activity, 3 = Staged, 4 = Core.
            Not sent by the item-store form of `getAvailableBadges`.
        - name: asNumericArray
          in: query
          required: false
          schema:
            type: string
            enum: [ y ]
          description: "`getAvailableBadges` — return `items` as a flat array."
        - name: liveOnly
          in: query
          required: false
          schema:
            type: string
            enum: [ y ]
          description: "`getAvailableBadges` — only include current (live) badge versions."
        - name: addGroupToName
          in: query
          required: false
          schema:
            type: string
            enum: [ y ]
          description: "`getAvailableBadges` — prefix `name` with the badge group where one is set."
        - name: noHidden
          in: query
          required: false
          schema:
            type: string
            enum: [ y ]
          description: "`getColumnsForBadge` — exclude hidden columns."
        - name: editable
          in: query
          required: false
          schema:
            type: string
            enum: [ y ]
          description: "`getColumnsForBadge` — only include columns that can be edited."
        - name: badge_id
          in: query
          required: false
          schema:
            type: string
          description: Badge ID (required for `getBadgeRecords` and `getColumnsForBadge`).
        - name: badge_version
          in: query
          required: false
          schema:
            type: string
          description: Badge version (required for `getBadgeRecords` and `getColumnsForBadge`).
        - name: payload
          in: query
          required: false
          schema:
            type: string
            enum: [ "1" ]
          description: Include full payload (observed value `1`).
        - name: context
          in: query
          required: false
          schema:
            type: string
            enum: [ none ]
          description: Context (observed value `none` for `getAvailableBadges`).
        - name: member_id
          in: query
          required: false
          schema:
            type: string
          description: Member ID filter (observed value `0` for all members).
      responses:
        "200":
          description: Badge records or available badges.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/BadgeRecordsResponse"
                  - $ref: "#/components/schemas/ItemStore"
              examples:
                availableBadgesItemStore:
                  summary: Available badges (asNumericArray=y&liveOnly=y&addGroupToName=y)
                  value:
                    identifier: badge_identifier
                    label: name
                    items:
                      - badge_id: "1001"
                        badge_version: "0"
                        shortname: "1001"
                        badge_identifier: "1001_0"
                        name: "Teamwork"
                        picture: "sites/onlinescoutmanager.co.uk/badge_images/img_000000.png"
                        description: "Work as part of a team on a range of activities."
                        parents_description: ""
                        config: "{\"requires\":[[\"a\"],[\"b\"]]}"
                        portal_config: "{\"always_show_in_list\":false}"
                        userid: "1"
                        sharing: "default-locked"
                        latest: "0"
                        badge_order: "0"
                        group_name: ""
                        created_at: "2015-01-27 10:00:00"
                        lastupdated: "2025-01-01 10:00:00"
                        type_id: "1"
                        badge_id_version: "1001_0"
                columnsForBadge:
                  summary: Requirement columns for a badge (action=getColumnsForBadge)
                  value:
                    identifier: column_id
                    label: label
                    items:
                      - label: "Team game"
                        description: "Take part in a team game."
                        column_id: "200001"
                        field_type: "text"
                        points: "0"
                        module_id: "3001"
                        module_letter: "a"
                        custom_columns: "0"
                        section_id: "0"
                        sameas_column_id: "0"
                        same_as: [ ]
                        custom: false
                availableBadges:
                  summary: Available badges list
                  value:
                    status: true
                    error: null
                    data:
                      - badge_id: 1001
                        badge_version: 0
                        name: "Teamwork"
                        picture: "badges/teamwork.png"
                        type_id: 1
                        config: "{}"
                        has_stock: false
                    meta: [ ]
                badgeRecords:
                  summary: Badge records for a specific badge
                  value:
                    status: true
                    error: null
                    data:
                      - scoutid: 3100001
                        firstname: "Jamie"
                        lastname: "River"
                        completed: "1"
                        awarded: "1"
                        status: 1
                    meta: [ ]
  /ext/badges/badgesbyperson/:
    get:
      summary: Load badges grouped by member
      tags:
        - Badges
      description: |
        Returns badge progress grouped by member. Each member entry includes
        an array of their badge statuses across all badge types.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ loadBadgesByMember ]
          description: Must be `loadBadgesByMember`.
        - name: section
          in: query
          required: true
          schema:
            type: string
            enum: [ squirrels, beavers, cubs, scouts, explorers ]
          description: Section type.
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: term_id
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
      responses:
        "200":
          description: Badges by member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BadgesByMemberResponse"
              example:
                status: true
                error: null
                data:
                  - scoutid: 3100001
                    firstname: "Jamie"
                    lastname: "River"
                    patrol: "Blue"
                    active: true
                    badges:
                      - badge_id: 1001
                        badge_version: 0
                        completed: "1"
                        awarded: "1"
                        status: 1
                meta: [ ]
  /ext/members/email/:
    get:
      summary: Get selected contact emails, or the email attachments manifest
      tags:
        - Email
      description: |
        - `getSelectedEmailsFromContacts` — resolves the email addresses for every
          member in the section using their default contact groups. The web UI
          now sends this as a **POST** with an explicit member list (see below);
          the GET form is kept here as previously observed.
        - `emailerAttachmentsManifest` — returns the attachment manifest for an
          email being composed. The `guid` / `id` identify the draft (the same
          GUID later sent to `sendTemplate` and `/v3/members/email/draft`). See
          [File manifests](#tag/Files) for the response shape.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getSelectedEmailsFromContacts, emailerAttachmentsManifest ]
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID (`getSelectedEmailsFromContacts`).
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID (`emailerAttachmentsManifest`).
        - name: id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Draft email GUID (`emailerAttachmentsManifest`).
        - name: guid
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Draft email GUID — same value as `id` (`emailerAttachmentsManifest`).
        - name: path
          in: query
          required: false
          schema:
            type: string
          description: Directory within the manifest (observed `/`).
        - name: temp
          in: query
          required: false
          schema:
            type: string
            enum: [ "true", "false" ]
          description: Include uncommitted (temporary) uploads.
      responses:
        "200":
          description: Selected emails for contacts, or a file manifest.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/SelectedContactEmails"
                  - $ref: "#/components/schemas/FileManifestResponse"
              example:
                emails:
                  "3100001":
                    emails:
                      - "parent@example.org"
                      - "guardian@example.org"
                    firstname: "Jamie"
                    lastname: "River"
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    member_id: 3100001
                count: 2
                blocks: [ ]
                daily_limit_reached: false
    post:
      summary: Resolve recipient emails or send an email
      tags:
        - Email
      description: |
        Two `action` values share this POST endpoint:

        **`getSelectedEmailsFromContacts`** (`sectionid` in the query string) —
        resolves the addresses for a chosen set of members and contact groups.
        Body fields:

        | Field | Format | Notes |
        |-------|--------|-------|
        | `scouts` | comma-separated member IDs | e.g. `3100001,3100002` |
        | `contactGroups` | JSON array of contact group identifiers | e.g. `["contact_primary_1","contact_primary_2","contact_primary_member"]` |

        The response is the same shape as the GET form (keyed by member ID).
        `count` is the number of addresses, not members.

        **`sendTemplate`** — sends (or schedules) an email. `emails` is the JSON
        object returned by `getSelectedEmailsFromContacts` (optionally with an
        `order` per member); `edits` is a JSON object of template regions
        `{ regionId: { blockIndex: { "text": "<html>" } } }`. For a plain email
        the region ID is `"0"`; when a campaign template is used, the region IDs
        are the campaign's `snippet_id`s (see
        `/v3/members/email/campaigns/snippets/{campaignId}`).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getSelectedEmailsFromContacts, sendTemplate ]
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID (required for `getSelectedEmailsFromContacts`).
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
              properties:
                scouts:
                  type: string
                  description: "`getSelectedEmailsFromContacts` — comma-separated member IDs."
                contactGroups:
                  type: string
                  description: "`getSelectedEmailsFromContacts` — JSON array of contact group identifiers."
                subject:
                  type: string
                  description: "`sendTemplate` — email subject."
                from:
                  type: string
                  description: "`sendTemplate` — sender, formatted `Display Name <address>`."
                cc:
                  type: string
                  description: "`sendTemplate` — CC addresses (may be empty)."
                save_email_from:
                  type: string
                  enum: [ "true", "false" ]
                  description: "`sendTemplate` — remember `from` as the default sender."
                emails:
                  type: string
                  description: "`sendTemplate` — JSON object of recipients keyed by member ID."
                edits:
                  type: string
                  description: "`sendTemplate` — JSON object of template region content."
                sectionid:
                  type: string
                  description: "`sendTemplate` — section ID."
                current_section_id:
                  type: string
                  description: "`sendTemplate` — section the user is currently viewing."
                campaign_id:
                  type: string
                  description: "`sendTemplate` — campaign template ID, or `0` for a plain email."
                email_session_key:
                  type: string
                  description: "`sendTemplate` — observed `blank`."
                guid:
                  type: string
                  format: uuid
                  description: "`sendTemplate` — draft GUID (ties attachments to the email)."
                draft_email_id:
                  type: string
                  description: "`sendTemplate` — ID returned by `POST /v3/members/email/draft`."
                scheduled_email_id:
                  type: string
                  description: "`sendTemplate` — scheduled email being sent, or `0`."
            examples:
              getSelectedEmailsFromContacts:
                summary: Resolve recipient emails (action=getSelectedEmailsFromContacts)
                value:
                  scouts: "3100001,3100002"
                  contactGroups: "[\"contact_primary_1\",\"contact_primary_2\",\"contact_primary_member\"]"
              sendTemplate:
                summary: Send an email (action=sendTemplate)
                value:
                  cc: ""
                  from: "1st Riverside Cubs <cubs@example.org>"
                  save_email_from: "false"
                  subject: "Weekly Meeting Reminder"
                  emails: "{\"3100001\":{\"firstname\":\"Jamie\",\"lastname\":\"River\",\"member_id\":3100001,\"order\":0,\"photo_guid\":\"00000000-0000-0000-0000-000000000000\",\"emails\":[\"parent@example.org\"]}}"
                  edits: "{\"0\":{\"0\":{\"text\":\"<p>Don't forget to bring your swimming kit this week!</p>\"}}}"
                  sectionid: "45002"
                  current_section_id: "45002"
                  campaign_id: "0"
                  email_session_key: "blank"
                  guid: "00000000-0000-0000-0000-000000000001"
                  draft_email_id: "80000001"
                  scheduled_email_id: "0"
      responses:
        "200":
          description: Selected emails (`getSelectedEmailsFromContacts`) or send confirmation (`sendTemplate`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/SelectedContactEmails"
                  - $ref: "#/components/schemas/OkResponse"
              examples:
                sendTemplate:
                  summary: Email sent
                  value:
                    ok: true
  /ext/members/patrols/:
    get:
      summary: Get patrols with members
      tags:
        - Members
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getPatrolsWithPeople ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
        - name: include_no_patrol
          in: query
          required: false
          schema:
            type: string
            enum: [ y, n ]
          description: Include pseudo-patrol for members not in a patrol.
      description: |
        Returns patrols (sixes, lodges, etc.) keyed by patrol ID, each with its
        members. Negative IDs are pseudo-patrols: `-2` = Leaders, `-3` = Young
        Leaders. Members with a rank also carry `patrol_role_level` (the numeric
        rank, matching `patrolleader`) and `patrol_role_level_abbr` (e.g. `6er`,
        `2nd`); the `patrol` field then includes the abbreviation (e.g. `Blue 6er`).
      responses:
        "200":
          description: Patrol list keyed by patrol ID.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
              example:
                "-3":
                  patrolid: "-3"
                  sectionid: "-2"
                  name: "Young Leaders (YLs)"
                  active: "1"
                  points: "0"
                  census_costs: false
                  members: [ ]
                "-2":
                  patrolid: "-2"
                  sectionid: "-2"
                  name: Leaders
                  active: "1"
                  points: "0"
                  census_costs: false
                  members: [ ]
                "12001":
                  patrolid: "12001"
                  sectionid: "45001"
                  name: Blue
                  active: "1"
                  points: "2"
                  census_costs: false
                  members:
                    - firstname: Jamie
                      lastname: River
                      scout_id: 3100001
                      photo_guid: "00000000-0000-0000-0000-000000000000"
                      patrolid: 12001
                      patrolleader: "2"
                      patrol: "Blue 6er"
                      sectionid: 45001
                      enddate: null
                      age: "10 / 2"
                      patrol_role_level_label: Sixer
                      active: 1
                      scoutid: 3100001
                      patrol_role_level_abbr: "6er"
                      patrol_role_level: 2
                      editable: true
    post:
      summary: Update patrol points
      tags:
        - Members
      description: |
        Sets the points total for a patrol. `points` is the **new total**, not an
        increment. The response body is an empty JSON array on success.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ updatePatrolPoints ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [ patrolid, points ]
              properties:
                patrolid:
                  type: string
                  description: Patrol ID.
                points:
                  type: string
                  description: New points total.
            example:
              patrolid: "12001"
              points: "3"
      responses:
        "200":
          description: Empty array on success.
          content:
            application/json:
              schema:
                type: array
                items: { }
              example: [ ]
  /ext/members/census/:
    get:
      summary: Get census details
      tags:
        - Members
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getDetails ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
      responses:
        "200":
          description: Census detail response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CensusDetails"
              example:
                identifier: scoutid
                items:
                  - scoutid: "3100003"
                    firstname: Sam
                    lastname: Lake
                    joined: "2022-03-01"
                    photo_guid: null
                    sex: ""
                    ethnicity: ""
                    disabilities: "Edit disabilities"
                    myscout: "Send Parent Portal email"
                    raw_disabilities: [ ]
                    _filterString: "sam lake"
  /ext/members/flexirecords/:
    get:
      summary: Get flexi-records, their structure, or their data
      tags:
        - Members
      description: |
        Flexi-records are section-defined spreadsheets of extra per-member data.

        - `getFlexiRecords` — lists the section's flexi-records (`extraid` + `name`).
        - `getStructure` — returns a flexi-record's definition. `config` is a
          JSON-encoded string of column definitions (`[{"id":"f_1","name":"…"}]`)
          and `structure` is the Dojo grid layout (a frozen name block, then the
          editable `f_N` columns).
        - `getData` — returns one row per member for a term, with each custom
          column under its `f_N` key, plus `total` / `completed` summary columns.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getFlexiRecords, getStructure, getData ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: archived
          in: query
          required: false
          schema:
            type: string
            enum: [ y, n ]
          description: "`getFlexiRecords` — include archived flexi-records."
        - name: extraid
          in: query
          required: false
          schema:
            type: string
          description: Flexi-record ID (required for `getStructure` and `getData`).
        - name: termid
          in: query
          required: false
          schema:
            type: string
          description: Term ID (required for `getData`).
        - name: nototal
          in: query
          required: false
          schema:
            type: string
          description: "`getData` — observed sent empty."
      responses:
        "200":
          description: Flexi-record list, structure, or data.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/FlexiRecords"
                  - type: object
                    additionalProperties: true
              examples:
                getFlexiRecords:
                  summary: Flexi-record list (action=getFlexiRecords)
                  value:
                    identifier: extraid
                    label: name
                    items:
                      - extraid: "23001"
                        name: "Hike Log"
                getStructure:
                  summary: Flexi-record structure (action=getStructure)
                  value:
                    extraid: "23001"
                    sectionid: "45002"
                    name: "Hike Log"
                    config: "[{\"id\":\"f_1\",\"name\":\"Distance (km)\",\"width\":\"150\"}]"
                    total: "none"
                    extrafields: "[]"
                    archived: "0"
                    soft_deleted: "0"
                    archived_at: null
                    structure:
                      - rows:
                          - name: "First Name"
                            field: firstname
                            width: "150px"
                            formatter: boldFormatter
                          - name: "Last Name"
                            field: lastname
                            width: "150px"
                        noscroll: true
                      - rows:
                          - name: "Distance (km)"
                            field: f_1
                            width: "150px"
                            formatter: boldFormatter
                            editable: true
                getData:
                  summary: Flexi-record data (action=getData)
                  value:
                    identifier: scoutid
                    items:
                      - scoutid: "3100001"
                        firstname: Jamie
                        lastname: River
                        dob: "2015-04-22"
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        patrolid: "12001"
                        total: ""
                        completed: ""
                        f_1: "12"
                        age: "10 / 04"
                        patrol: Blue
                        _filterString: "jamie river"
  /ext/members/attendance/:
    get:
      summary: Get attendance data, register structure, or badge requirements
      tags:
        - Attendance
      description: |
        Attendance endpoints share this path with different `action` values.

        - `get` — the register: one row per member with a `yyyy-mm-dd` key for
          each meeting they have a mark for (`Yes` / `No`), plus `meetings`
          (date → meeting title), `visitors` and `parent_rota` (both objects,
          empty when unused).
        - `structure` — the Dojo grid layout for the register: a frozen name
          block, then one column per meeting date (`tooltip` is the meeting title).
        - `getAttendanceBadgeRequirements` — badge requirements linked to the
          meeting on `date`. With `v=2` the response is an object with `badges`
          (the requirements) and `badges_updated_at` (who last changed the
          meeting's badge links); without `v` an array of requirements is returned.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ get, structure, getAttendanceBadgeRequirements ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: false
          schema:
            type: string
          description: Term ID (required for `get` and `structure`).
        - name: section
          in: query
          required: true
          schema:
            type: string
          description: Section type (e.g., `cubs`).
        - name: nototal
          in: query
          required: false
          schema:
            type: string
          description: Toggle total attendance column (observed `true`).
        - name: date
          in: query
          required: false
          schema:
            type: string
          description: Meeting date (required for `action=getAttendanceBadgeRequirements`).
        - name: v
          in: query
          required: false
          schema:
            type: string
            enum: [ "2" ]
          description: "`getAttendanceBadgeRequirements` — response version. `2` returns the `{badges, badges_updated_at}` object."
      responses:
        "200":
          description: Attendance, structure, or badge requirement response.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/AttendanceList"
                  - $ref: "#/components/schemas/AttendanceBadgeRequirements"
                  - $ref: "#/components/schemas/AttendanceBadgeRequirementsV2"
                  - $ref: "#/components/schemas/GridStructure"
              examples:
                attendanceList:
                  summary: Attendance list (action=get)
                  value:
                    identifier: scoutid
                    label: name
                    items:
                      - "2025-01-07": "Yes"
                        "2025-01-14": "No"
                        total: 1
                        firstname: "Jamie"
                        lastname: "River"
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        patrolid: 12001
                        patrolleader: "2"
                        patrol: "Blue"
                        dob: "2015-04-22"
                        sectionid: 45002
                        startdate: "2021-09-01"
                        enddate: null
                        age: "10 / 04"
                        patrol_role_level_label: "Sixer"
                        active: true
                        scoutid: 3100001
                        _filterString: "jamie river blue"
                      - "2025-01-07": "Yes"
                        total: 1
                        firstname: "Robert"
                        lastname: "Baden Powell"
                        patrolid: -2
                        patrol: "Leaders"
                        active: true
                        scoutid: 3100010
                    visitors: { }
                    parent_rota: { }
                    meetings:
                      "2025-01-07": "Welcome Back!"
                      "2025-01-14": "Camp Skills Night"
                structure:
                  summary: Register grid layout (action=structure)
                  value:
                    - rows:
                        - name: "First Name"
                          field: firstname
                          width: "100px"
                          formatter: registerBoldFormatter
                        - name: "Last Name"
                          field: lastname
                          width: "100px"
                          formatter: registerBoldFormatter
                      noscroll: true
                    - rows:
                        - name: "2025-01-07"
                          field: "2025-01-07"
                          formatter: doneFormatter
                          width: "110px"
                          tooltip: "Welcome Back!"
                badgeRequirementsV2:
                  summary: Attendance badge requirements (v=2)
                  value:
                    badges:
                      - badge: "0"
                        badge_id: "1002"
                        badge_version: "0"
                        column_id: "200101"
                        badgeName: "Chef"
                        badgetype: "activity"
                        columngroup: "A"
                        columnname: "a"
                        description: "Learn about the main food groups."
                        data: "14/01/2025"
                        eveningid: 9500002
                        meetingdate: "2025-01-14"
                        link_from: "meeting"
                        name: "Food Groups"
                        section: "cubs"
                        sameas_counter: 0
                        sectionid: 45002
                    badges_updated_at:
                      - user_name: "Alex Scout"
                        date_time: "14/01/2025 20:40"
                badgeRequirements:
                  summary: Attendance badge requirements (no v parameter)
                  value:
                    - badge_id: "1593"
                      badgeName: "Communicator"
                      column_id: "100542"
                      name: "Code"
                      meetingdate: "2025-01-14"
                      section: "cubs"
                      sectionid: 12345
    post:
      summary: Update attendance
      tags:
        - Attendance
      description: |
        Marks one or more members present/absent for a meeting date, optionally
        awarding the badge requirements linked to that meeting. The response
        body is the bare JSON literal `true` on success.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ update ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
        - name: termid
          in: query
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                scouts:
                  type: string
                  description: JSON array of member IDs.
                selectedDate:
                  type: string
                present:
                  type: string
                  description: "`Yes`, `No`, or `Absent` (observed)."
                section:
                  type: string
                sectionid:
                  type: string
                completedBadges:
                  type: string
                  description: JSON array of badge data.
                customData:
                  type: string
                  description: JSON array of custom data updates.
              required: [ scouts, selectedDate, present, section, sectionid ]
              additionalProperties: true
            example:
              scouts: "[3100001]"
              selectedDate: "2025-01-14"
              present: "Yes"
              section: "cubs"
              sectionid: "45002"
              completedBadges: "[]"
              customData: "[]"
      responses:
        "200":
          description: The JSON literal `true` on success.
          content:
            application/json:
              schema:
                type: boolean
              example: true
  /v3/members/attendance/visitors/{sectionid}:
    post:
      summary: Add or update a register visitor
      tags:
        - Attendance
      description: |
        Records a non-member visitor (or parent helper) against a meeting date.
        Visitors appear in the `visitors` object of
        `GET /ext/members/attendance/?action=get`.
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [ name, meeting_date, present, type ]
              properties:
                name:
                  type: string
                  description: Visitor's name (free text).
                meeting_date:
                  type: string
                  format: date
                present:
                  type: string
                  enum: [ "Yes", "No" ]
                type:
                  type: string
                  description: Observed `visitor`.
            example:
              name: "Visiting Leader"
              meeting_date: "2025-01-14"
              present: "Yes"
              type: visitor
      responses:
        "200":
          description: Visitor saved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta: [ ]
  /ext/customdata/:
    get:
      summary: Get custom data for a member
      tags:
        - Custom Data
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getData ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: associated_id
          in: query
          required: true
          schema:
            type: string
          description: Member ID.
        - name: associated_type
          in: query
          required: true
          schema:
            type: string
            enum: [ member ]
        - name: associated_is_section
          in: query
          required: false
          schema:
            nullable: true
            type: string
          description: Section association flag (observed null).
        - name: varname_filter
          in: query
          required: false
          schema:
            nullable: true
            type: string
          description: Filter by variable name (observed null).
        - name: context
          in: query
          required: true
          schema:
            type: string
            enum: [ members ]
        - name: group_order
          in: query
          required: false
          schema:
            type: string
          description: Group ordering (observed "section").
      responses:
        "200":
          description: Custom data groups and columns.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomDataResponse"
              example:
                status: true
                error: null
                data:
                  - group_id: 7
                    group_type: "floating"
                    identifier: "floating"
                    name: "Other"
                    columns:
                      - column_id: 34
                        type: "select"
                        varname: "gender"
                        label: "Gender"
                        value: "Male"
                meta:
                  group_name: "1st Riverside Scouts"
                  section_name: "Cubs"
    post:
      summary: Update custom data for a member
      tags:
        - Custom Data
      description: |
        Updates custom data fields on a member. Two `action` values are supported:

        - `update` — bulk update using `data[field]=value` pairs (e.g. contact details).
        - `updateColumn` — update a single custom data column by `column_id`.

        Column IDs are section-specific. The only well-known core column is
        `column_id=34` for Gender (values: Male, Female, Self-identify,
        Prefer not to say, Other, Unspecified). Other column IDs vary per
        section — inspect HTTP traffic to discover them.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ update, updateColumn ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
              properties:
                associated_id:
                  type: string
                  description: Member scout ID.
                associated_type:
                  type: string
                  enum: [ member ]
                associated_is_section:
                  type: string
                  description: Section association flag (observed `false`).
                group_id:
                  type: string
                  description: |
                    Custom data group ID. For `update` action this matches
                    the contact group (1–6). For `updateColumn` this is the
                    custom data group containing the column.
                column_id:
                  type: integer
                  description: Column ID to update (`updateColumn` action only).
                value:
                  type: string
                  description: New value for the column (`updateColumn` action only).
                context:
                  type: string
                  enum: [ members ]
            examples:
              bulkUpdate:
                summary: Bulk update contact details (action=update)
                value:
                  associated_id: "3100003"
                  associated_type: "member"
                  group_id: "2"
                  context: "members"
                  service: "desktop"
                  "data[firstname]": "Hazel"
                  "data[lastname]": "Park"
              updateColumn:
                summary: Update a single custom column (action=updateColumn)
                value:
                  associated_type: member
                  associated_id: "3100005"
                  associated_is_section: "false"
                  group_id: "7"
                  column_id: 34
                  value: "Male"
                  context: members
      responses:
        "200":
          description: Custom data update response.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/CustomDataUpdateResponse"
                  - $ref: "#/components/schemas/CustomDataColumnUpdateResponse"
              examples:
                bulkUpdate:
                  summary: Bulk update response
                  value:
                    status: true
                    error: null
                    data:
                      lastname: "Park"
                    meta:
                      last_updated:
                        last_updated: "2025-09-12 18:30:45"
                        last_updated_by_name: "Alex Scout"
                      updated:
                        lastname: "Last Name"
                columnUpdate:
                  summary: Column update response
                  value:
                    status: true
                    error: null
                    data:
                      column_id: 34
                      type: select
                      required: "no"
                      varname: gender
                      label: Gender
                      value: Male
                      is_core: "yes"
                      order: "12"
                    meta:
                      last_updated:
                        last_updated: "2025-09-15 19:43:22"
                        last_updated_by_name: "Alex Scout"
  /ext/audittrail/:
    post:
      summary: Get audit trail for a member
      tags:
        - Members
      description: |
        Returns the audit trail (change history) for a member. Despite being
        a POST, this is a read-only query — the body specifies which member
        to look up. The response contains a chronological list of changes
        including patrol moves, rank promotions, field edits, and section
        additions.

        Each entry has a `type` and `code` describing the kind of change:

        | type | code | Meaning |
        |------|------|---------|
        | `members` | `add` | Member added to section |
        | `details` | `field` | A member field was edited |
        | `details` | `new` | A new detail was added (e.g. consent) |
        | `patrols` | `add-to` | Member added to a patrol/six |
        | `patrol-leader` | `promoted` | Rank promotion or demotion |

        The `summary` field contains HTML with `<strong>` tags highlighting
        names, fields, and values.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getAuditTrail ]
          description: Must be `getAuditTrail`.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required:
                - section_id
                - id
                - type
              properties:
                section_id:
                  type: string
                  description: Section ID (repeated in body).
                id:
                  type: string
                  description: Member scout ID.
                type:
                  type: string
                  enum: [ member ]
                  description: Must be `member`.
            example:
              section_id: "45001"
              id: "3100001"
              type: member
      responses:
        "200":
          description: Audit trail entries for the member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuditTrailResponse"
              example:
                status: true
                error: null
                data:
                  - date: "15/09/2025 19:42"
                    date_raw: "2025-09-15 19:42:10"
                    summary: "<strong>Jamie River</strong> has had their <strong>Consents: Photographs</strong> added as <strong>Yes</strong> by <strong>Alex Scout</strong>"
                    verbose: ""
                    type: details
                    code: new
                    hidden: false
                  - date: "15/09/2025 19:38"
                    date_raw: "2025-09-15 19:38:31"
                    summary: "<strong>Jamie River</strong> is now a <strong>Sixer</strong> in the Blue six in <strong>Cubs</strong> (previously <strong>Seconder</strong>), changed by <strong>Alex Scout</strong>."
                    verbose: ""
                    type: patrol-leader
                    code: promoted
                    hidden: false
                  - date: "15/09/2025 19:37"
                    date_raw: "2025-09-15 19:37:04"
                    summary: "<strong>Jamie River</strong> was added to the <strong>Blue</strong> six."
                    verbose: ""
                    type: patrols
                    code: add-to
                    hidden: false
                  - date: "15/09/2025 19:35"
                    date_raw: "2025-09-15 19:35:54"
                    summary: "<strong>Alex Scout</strong> updated <strong>Date of Birth</strong> for <strong>Jamie River</strong> from <strong>22/04/2015</strong> to <strong>23/04/2015</strong>"
                    verbose: ""
                    type: details
                    code: field
                    hidden: false
                  - date: "15/09/2025 19:34"
                    date_raw: "2025-09-15 19:34:07"
                    summary: "<strong>Jamie River</strong> has been added to <strong>1st Riverside Scouts: Cubs</strong> by <strong>Alex Scout</strong>."
                    verbose: ""
                    type: members
                    code: add
                    hidden: false
                meta:
                  hidden: false
                  note_types:
                    - notes
                    - received-email
                    - received-phonecall
                    - received-letter
                    - sent-email
                    - sent-phonecall
                    - sent-letter
  /ext/settings/emails/:
    get:
      summary: Get email configuration or delivery reports for a section
      tags:
        - Email
      description: |
        - `getConfig` — the email/newsletter configuration for a section,
          including available events and patrols that can be used as recipient
          filters when composing emails. Also indicates whether the section has
          a newsletter slug configured and if it is live.
        - `getDeliveryReport` — the section's sent (and, with
          `include_scheduled=true`, scheduled) emails. The response is a **bare
          array**: the first element is an `ALL` summary row
          (`{id: "0", name: "ALL", type: "all", count}`), followed by one row per
          email with send status, recipient/error/open counts and tracked links.
        - `getDeliveryReportRows` — per-recipient delivery status for one email
          (`email_id`). `status` is the display status (`sent`, `opened`, …) and
          `status_raw` the provider status (`delivered`, `open`, …).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getConfig, getDeliveryReport, getDeliveryReportRows ]
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID (`getConfig`, `getDeliveryReport`).
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID (`getDeliveryReportRows`).
        - name: email_id
          in: query
          required: false
          schema:
            type: string
          description: Email ID from `getDeliveryReport` (required for `getDeliveryReportRows`).
        - name: generic_date
          in: query
          required: false
          schema:
            type: string
            enum: [ "true" ]
          description: "`getDeliveryReport` — return `date` as `yyyy-mm-dd hh:mm:ss`."
        - name: include_scheduled
          in: query
          required: false
          schema:
            type: string
            enum: [ "true" ]
          description: "`getDeliveryReport` — include scheduled (not yet sent) emails."
      responses:
        "200":
          description: Email configuration, delivery report, or delivery report rows.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/EmailConfig"
                  - type: array
                    items:
                      type: object
                      additionalProperties: true
                  - $ref: "#/components/schemas/V3Envelope"
              examples:
                getConfig:
                  summary: Email configuration (action=getConfig)
                  value:
                    found: true
                    result:
                      sectionid: "45001"
                      slug: "1stRiversideCubs"
                      numemails: "0"
                      live: "1"
                    events:
                      - name: "Summer Camp 2025"
                        slug: "SummerCamp2025"
                      - name: "Investiture Ceremony"
                        slug: "InvestitureCeremony"
                    patrols:
                      - name: "Blue"
                        slug: "Blue"
                      - name: "Green"
                        slug: "Green"
                    leaders: true
                getDeliveryReport:
                  summary: Delivery report (action=getDeliveryReport)
                  value:
                    - id: "0"
                      name: "ALL"
                      type: "all"
                      count: 1
                    - id: 80000001
                      type: "email"
                      date: "2025-01-10 13:05:00"
                      subject: "Weekly Meeting Reminder"
                      status: "SENT"
                      sent: 2
                      errors: 0
                      warnings: 0
                      opens: 1
                      hascontent: true
                      config: null
                      scheduled_recipient_count: 0
                      failure_reason: null
                      from_email: "cubs@example.org"
                      has_attachments: false
                      links:
                        - url: "https://www.example.org/kit-list"
                          click_count: 0
                          last_clicked: null
                      user_id: 100001
                      photo_url: false
                      from_name: "Alex Scout"
                getDeliveryReportRows:
                  summary: Per-recipient status (action=getDeliveryReportRows)
                  value:
                    status: true
                    error: null
                    data:
                      - emailid: "80000001"
                        sendtime: "10/01/2025 13:05"
                        subject: "Weekly Meeting Reminder"
                        email: "parent@example.org"
                        status: "opened"
                        lastupdated: "10/01/2025 17:46"
                        member_id: "3100001"
                        hascontent: true
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        firstname: "Jamie"
                        lastname: "River"
                        status_raw: "open"
                        id: "0123456789abcdef0123456789abcdef"
                        email_key: "parentexampleorg"
                    meta:
                      ids: [ "0123456789abcdef0123456789abcdef" ]
                      total: 1
                      has_attachments: false
                      can_be_forwarded_status: "empty"
                      itemstore:
                        identifier: id
  /v3/members/contact/{sectionid}/removalWarnings:
    post:
      summary: Get pre-removal warnings for a member
      tags:
        - Members
      description: |
        Checks whether there are any warnings before removing a member from a
        section (e.g. outstanding payments). Call this before `removeMember`
        to display confirmation prompts to the user.
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required:
                - "member_ids[0]"
              properties:
                "member_ids[0]":
                  type: integer
                  description: Scout ID of the member to check. Additional members can be sent as `member_ids[1]`, `member_ids[2]`, etc.
            example:
              "member_ids[0]": 3100005
      responses:
        "200":
          description: Removal warnings.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                  error:
                    nullable: true
                  data:
                    type: object
                    properties:
                      payments:
                        type: boolean
                        description: Whether the member has outstanding payments.
                    additionalProperties: true
                  meta:
                    type: object
                    properties:
                      ok:
                        type: boolean
              example:
                status: true
                error: null
                data:
                  payments: false
                meta:
                  ok: true
  /v3/members/review/deletion/{sectionid}:
    get:
      summary: Get deletable members
      tags:
        - Members
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
        - name: mode
          in: query
          required: false
          schema:
            type: string
          description: Display mode (observed value `grid`).
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID (alternate naming).
      responses:
        "200":
          description: Deletable members response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletableMembers"
              example:
                status: true
                error: null
                data:
                  - firstname: Pat
                    lastname: Stone
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    date_deleted: "2023-10-01"
                    left_section: "2023-07-20"
                    id: 3100004
                meta: [ ]
  /v3/members/review/elsewhere/{sectionid}:
    get:
      summary: Get members who left this section and are elsewhere
      tags:
        - Members
      description: |
        Members who have left this section (`date_left`) but are still active in
        other sections, listed with the names of those sections — typically
        members who have moved on to the next section.
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      responses:
        "200":
          description: Members now in other sections.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 3100007
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    firstname: Morgan
                    lastname: Hill
                    date_left: "2025-04-11"
                    sections:
                      - "1st Riverside Scouts: Scouts"
                meta: [ ]
  /v3/members/review/future_transfers/{sectionid}:
    get:
      summary: Get scheduled (future) member transfers
      tags:
        - Members
      description: |
        Lists every current member with their scheduled transfer, if any
        (`future_transfer` is `null` when none is set), plus `incoming` members
        due to transfer into this section.
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      responses:
        "200":
          description: Current and incoming future transfers.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  current:
                    - id: 3100001
                      firstname: Jamie
                      lastname: River
                      photo_guid: "00000000-0000-0000-0000-000000000000"
                      dob: "2015-04-22"
                      future_transfer:
                        id: 600001
                        to_section_id: 45004
                        to_group_name: "1st Riverside Scouts"
                        to_section_name: "1st Riverside Scouts: Scouts"
                        transfer_date: "2025-12-19"
                    - id: 3100002
                      firstname: Riley
                      lastname: Park
                      photo_guid: "00000000-0000-0000-0000-000000000000"
                      dob: "2016-06-01"
                      future_transfer: null
                  incoming: [ ]
                meta:
                  can_edit: true
  /ext/programme/:
    get:
      summary: Programme endpoints
      tags:
        - Programme
      description: |
        Programme endpoints share this path with different `action` values.
        Use `getProgrammeSummary` for term overview, `getProgramme` for details,
        `getMembersForParentRota` for parent rota members, or
        `programmeAttachmentsManifest` for attachment metadata.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum:
              [
                getProgrammeSummary,
                getProgramme,
                programmeAttachmentsManifest,
                getMembersForParentRota,
              ]
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: false
          schema:
            type: string
          description: Term ID.
        - name: eveningid
          in: query
          required: false
          schema:
            type: string
          description: Meeting ID.
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID.
        - name: id
          in: query
          required: false
          schema:
            type: string
          description: Meeting ID.
        - name: evening_id
          in: query
          required: false
          schema:
            type: string
          description: Meeting ID.
        - name: verbose
          in: query
          required: false
          schema:
            type: string
          description: Set to `1` to add `badges` and `unavailable_leaders` to each summary item.
        - name: path
          in: query
          required: false
          schema:
            type: string
          description: "`programmeAttachmentsManifest` — directory within the manifest (observed `/`)."
        - name: temp
          in: query
          required: false
          schema:
            type: string
          description: "`programmeAttachmentsManifest` — include uncommitted uploads (observed `false`)."
        - name: upload_mode
          in: query
          required: false
          schema:
            type: string
          description: "`programmeAttachmentsManifest` — observed `programme`."
      responses:
        "200":
          description: Programme response.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ProgrammeSummary"
                  - $ref: "#/components/schemas/ProgrammeDetails"
                  - $ref: "#/components/schemas/ProgrammeParentRotaMembers"
                  - $ref: "#/components/schemas/FileManifestResponse"
              examples:
                programmeSummary:
                  summary: Programme summary
                  value:
                    items:
                      - eveningid: "9500001"
                        title: "Camp Skills Night"
                        parentsrequired: "1"
                        meetingdate: "2024-11-01"
                        parentsattendingcount: "0"
                        primary_leader: false
                programmeSummaryVerbose:
                  summary: Programme summary with verbose=1
                  value:
                    items:
                      - eveningid: "9500001"
                        title: "Camp Skills Night"
                        parentsrequired: "0"
                        meetingdate: "2024-11-01"
                        location: null
                        parentsattendingcount: "0"
                        primary_leader: false
                        badges:
                          - name: "Teamwork"
                            img: "sites/onlinescoutmanager.co.uk/badge_images/img_000000.png"
                        unavailable_leaders: 0
                programmeDetails:
                  summary: Programme details (getProgramme with sectionid, termid and eveningid)
                  value:
                    items:
                      - eveningid: "9500001"
                        sectionid: "45001"
                        title: "Camp Skills Night"
                        notesforparents: "Bring a warm coat."
                        notesforhelpingparents: null
                        parentsrequired: "1"
                        games: ""
                        prenotes: ""
                        postnotes: ""
                        leaders: ""
                        meetingdate: "2024-11-01"
                        starttime: "18:00:00"
                        endtime: "19:30:00"
                        primary_leader_id: "0"
                        config:
                          has_badges: true
                          badges_updated_at:
                            - user_id: 100001
                              date_time: "2024-10-25 19:56:16"
                        soft_deleted: "0"
                        latitude: null
                        longitude: null
                        location: null
                        help:
                          - scoutid: "3100005"
                            scout: "Quinn Wood"
                        unavailableleaders: [ ]
                        activities: [ ]
                        badgelinks:
                          - picture: "sites/onlinescoutmanager.co.uk/badge_images/img_000000.png"
                            link_relation_id: "40000001"
                            badge_id: "1001"
                            badge_version: "0"
                            column_id: "200001"
                            badge: "1001_0"
                            badgeLongName: "Teamwork"
                            badgetype: "challenge"
                            badgetypeLongName: "Challenge"
                            columnname: "200001"
                            columnnameLongName: "B: Examples = Camp Skills Night"
                            data: "Camp Skills Night"
                            section: "cubs"
                            same_as: [ ]
                            sectionLongName: "Cubs"
                            label: "Challenge: Teamwork - Examples"
                        seasonal_suggestions:
                          - activityid: "10"
                            title: "Team Building"
                            description: "Various team building activities."
                            runningtime: "60"
                            location: "indoors"
                            shared: "2"
                            visibility: "all"
                            site_type: "scouts"
                            rating: 86
                            icons: [ "popular", "runningtime-60" ]
                            tags: [ "team building" ]
                            review_summary:
                              writeup:
                                rating: 4.5
                                number_reviews: 3
                              activity:
                                rating: 4.5
                                number_reviews: 120
                    badgelinks: { }
                programmeParentRotaMembers:
                  summary: Parent rota members
                  value:
                    status: true
                    error: null
                    data:
                      - scoutid: "3100010"
                        firstname: "Robert"
                        lastname: "Baden Powell"
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        patrol_id: -2
                    meta: [ ]
    post:
      summary: Update programme meeting parts
      tags:
        - Programme
      description: |
        Fields left out of `parts` are unchanged. Unlike `getProgramme`, the
        response is a flat object with no `items` wrapper or `status`.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ editEveningParts ]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                sectionid:
                  type: string
                eveningid:
                  type: string
                parts:
                  type: string
                  description: |
                    JSON-encoded object of `ProgrammeMeeting` fields to change. Observed:
                    `title`, `prenotes`, `games`, `postnotes`, `notesforparents`.
              required: [ sectionid, eveningid, parts ]
              additionalProperties: true
            examples:
              title:
                summary: Rename a meeting
                value:
                  sectionid: "12345"
                  eveningid: "9464403"
                  parts: "{\"title\":\"Astronomy Night 2\"}"
              clear:
                summary: Clear the notes for parents
                value:
                  sectionid: "12345"
                  eveningid: "9464403"
                  parts: "{\"notesforparents\":\"\"}"
      responses:
        "200":
          description: The full meeting after the update.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProgrammeMeeting"
              example:
                eveningid: "9464403"
                sectionid: "12345"
                title: "Astronomy Night"
                notesforparents: "Bring a torch."
                notesforhelpingparents: null
                parentsrequired: "0"
                games: "Stars and planets tag"
                prenotes: "Set up the telescope before the meeting."
                postnotes: ""
                leaders: ""
                meetingdate: "2025-01-21"
                starttime: "17:30:00"
                endtime: "18:45:00"
                primary_leader_id: "1000001"
                config:
                  has_badges: false
                soft_deleted: "0"
                help: [ ]
                unavailableleaders: [ ]
                activities: [ ]
                badgelinks: [ ]
                seasonal_suggestions:
                  - activityid: "10"
                    title: "Team Building"
                    description: "Various team building activities."
  /ext/programme/clouds/:
    get:
      summary: Get badge tag cloud
      tags:
        - Badges
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getBadgeTagCloud ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
        - name: section
          in: query
          required: true
          schema:
            type: string
            enum: [ squirrels, beavers, cubs, scouts, explorers ]
          description: Section type.
      responses:
        "200":
          description: Badge tag cloud response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BadgeTagCloud"
              example:
                tags:
                  community: 1
                tag_count: 1
                badges:
                  "sites/onlinescoutmanager.co.uk/badge_images/img_000000.png": 1
  /ext/dashboard/:
    get:
      summary: Get next things dashboard data
      tags:
        - Dashboard
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getNextThings ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
        - name: section
          in: query
          required: true
          schema:
            type: string
            enum: [ squirrels, beavers, cubs, scouts, explorers, adults ]
          description: Section type.
        - name: parts
          in: query
          required: false
          schema:
            type: string
            enum: [ core, agenda, section_widgets, user_widgets ]
          description: |
            Returns only one slice of the dashboard. The web UI now makes four
            requests, one per part; every response echoes `parts` back. Keys
            observed per part:

            | `parts` | Keys returned |
            |---------|---------------|
            | `core` | `is_full_admin`, `duplicate_admin_names`, `permissions` |
            | `agenda` | `conf`, `patrols`, `birthdays`, `programme`, `events` |
            | `section_widgets` | `notepad`, `subscription_expiring_soon`, `members`, `outstandingpayments`, `shop_orders_to_dispatch`, `badge_homework`, `deletable_members`, `due_badges`, `meetings_needing_badge_updates`, `pending_applicant_count`, `thanks` |
            | `user_widgets` | `programme_reviews`, `badge_supplier_review_prompt`, `blocked_expense_card_transactions`, `chat_highlights`, `expense_cards`, `feature_request_voting`, `pending_expense_cardholder_application`, `recommendation`, `unanswered_question`, `user_to_do_tasks`, `whats_new` |

            Omit `parts` to receive the combined response shown in the example.
      responses:
        "200":
          description: Dashboard next things response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardNextThings"
              example:
                conf:
                  birthdays: 1
                  programme: 1
                  events: 1
                is_full_admin: true
                permissions:
                  badge: 20
                  member: 20
                  user: "100"
                patrols:
                  - patrolid: "12001"
                    sectionid: "45001"
                    name: Blue
                    active: "1"
                    points: "0"
                    census_costs: false
                birthdays:
                  - member_id: 3100006
                    firstname: Casey
                    lastname: Brook
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    dob: "2016-02-02"
                    age: 9
                programme:
                  - meetingdate: "2024-11-01"
                    title: "Camp Skills Night"
                    eveningid: "9500001"
                events:
                  - eventid: "1700001"
                    name: "District Camp"
                    date: "2025-06-19"
                    end_date: "2025-06-21"
                    yes: 1
                outstandingpayments:
                  - name: "Subscriptions"
                    amount: 45
                    scheme_id: "47001"
                    currency: "GBP"
                surveys: false
                notepad:
                  raw: "0"
                  html: "<p></p>"
                subscription_expiring_soon: false
                programme_reviews: [ ]
                deletable_members:
                  count: 2
                due_badges:
                  count: 5
                expense_cards:
                  cardholders: [ ]
                  number_cardholders: 0
                feature_request_voting:
                  has_feature_request: false
                pending_expense_cardholder_application:
                  data_required: false
                thanks:
                  messages: [ ]
                unanswered_question:
                  has_questions: false
                user_to_do_tasks:
                  tasks: [ ]
                  users: { }
                whats_new:
                  title: "Example Update"
                  msg: "<p>Example message.</p>"
                  story_id: 100
                  like_count: 0
                  user_has_liked: false
  /v3/risk_assessments/{sectionId}/categories:
    get:
      summary: Show programme risk assessment categories
      tags:
        - Risk Assessments
      parameters:
        - name: sectionId
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      responses:
        "200":
          description: Risk assessment categories response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RiskAssessmentCategories"
              example:
                status: true
                error: null
                data:
                  - name: "General Risks"
                meta:
                  itemstore:
                    identifier: name
  /ext/events/summary/:
    get:
      summary: List events or the event audit log
      tags:
        - Events
      description: |
        - `get` — events for the term with headline attendance counts
          (`yes` = total attending, split into `yes_members` / `yes_yls` /
          `yes_leaders`; plus `no`, `invited`, `reserved`, `shown`). Dates come
          in both UK (`dd/mm/yyyy`) and ISO (`startdate_g`) forms. `cost` is a
          decimal string in pounds.
        - `getEvents` — a lightweight list (`eventid`, `name`, `startdate`,
          `attachments`). With `onlyArchived=1`, returns archived events only.
        - `getAudit` — recent attendance changes made by parents through the
          Parent Portal (`type=portal`), newest first.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ get, getEvents, getAudit ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
          description: Section ID.
        - name: termid
          in: query
          required: false
          schema:
            type: string
          description: Term ID (`get`, `getEvents`).
        - name: onlyArchived
          in: query
          required: false
          schema:
            type: string
            enum: [ "1" ]
          description: "`getEvents` — only return archived events."
        - name: type
          in: query
          required: false
          schema:
            type: string
            enum: [ portal ]
          description: "`getAudit` — audit source (observed `portal`)."
      responses:
        "200":
          description: Dojo item store of events or audit entries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ItemStore"
              examples:
                get:
                  summary: Events with attendance counts (action=get)
                  value:
                    identifier: eventid
                    items:
                      - eventid: "1700001"
                        name: "Winter Sleepover"
                        date: "06/12/2025"
                        startdate_g: "2025-12-06"
                        startdate: "06/12/2025"
                        enddate: "07/12/2025"
                        starttime: "11:00:00"
                        endtime: "14:00:00"
                        cost: "40.00"
                        location: "Riverside Scout Hut"
                        approval_status: null
                        rota_offered: 0
                        rota_accepted: 0
                        rota_required: null
                        yes: 18
                        yes_members: 15
                        yes_yls: 0
                        yes_leaders: 3
                        reserved: 0
                        no: 2
                        absent: 0
                        invited: 4
                        shown: 0
                        x: 1
                getEvents:
                  summary: Archived events (action=getEvents&onlyArchived=1)
                  value:
                    identifier: eventid
                    label: name
                    items:
                      - eventid: "1700002"
                        name: "District Camp"
                        startdate: "2024-06-19"
                        attachments: false
                getAudit:
                  summary: Parent Portal changes (action=getAudit&type=portal)
                  value:
                    identifier: auditid
                    items:
                      - auditid: "50000001"
                        eventid: "1700001"
                        name: "Winter Sleepover"
                        firstname: "Jamie"
                        lastname: "River"
                        photo_guid: "00000000-0000-0000-0000-000000000000"
                        member_id: "3100001"
                        updatedate: "2025-11-07 13:16:11"
                        date: "07/11/2025 13:16"
                        generic_date: "2025-11-07 13:16:11"
                        updatedby: "Jamie"
                        type: "attendance"
                        desc: "Attendance: Yes"
  /v3/events/event/{eventId}/basic_details:
    get:
      summary: Get event basic details
      tags:
        - Events
      description: |
        Name, location, dates/times, cost (number, in **pounds**), and
        attendance limit. `meta` reports lock state (`past_locked`,
        `shared_locked`) and whether the event is on the Parent Portal.
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
      responses:
        "200":
          description: Event basic details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  name: "Winter Sleepover"
                  location: "Riverside Scout Hut"
                  start_date: "2025-12-06"
                  start_time: "11:00:00"
                  end_date: "2025-12-07"
                  end_time: "14:00:00"
                  cost: 40
                  attendance_limit: 24
                  attendance_limit_includes_leaders: false
                  extra:
                    at_home:
                      enabled: false
                      introduction: ""
                      parent_uploads_enabled: false
                      chat_enabled: false
                      activities: [ ]
                    sharing:
                      sharee: false
                      deleted: false
                  approval_status: null
                  properties: [ ]
                meta:
                  read_only: false
                  past_locked: false
                  shared_locked: false
                  maximum_attendance_limit: 0
                  has_parent_portal: true
  /v3/events/event/{eventId}/summary:
    get:
      summary: Get event summary
      tags:
        - Events
      description: |
        The event overview page. `data` holds the attending members, online
        payment totals, linked finance totals, "at home" activity counts and
        risk-assessment counts. `meta.event` is the full event record including
        **every invited member with their date of birth**, payment schedule
        (`payment_dates`) and kit list collection — treat as sensitive.

        Money units differ within this response: `cost` is in pounds, while
        `online_payments.received` / `expected` are also in pounds (expected =
        attendees × cost).
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
      responses:
        "200":
          description: Event summary.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  members:
                    - member_id: 3100001
                      full_name: "Jamie River"
                      photo_guid: "00000000-0000-0000-0000-000000000000"
                      patrol_id: 12001
                  reserved: [ ]
                  sharing: false
                  online_payments:
                    received: 40
                    expected: 720
                    number_payments: 1
                  transactions:
                    income: 0
                    expense: 0
                  at_home:
                    enabled: false
                    comments_count: 0
                    uploads_count: 0
                    activities_count: 0
                  risk_assessments:
                    event: 0
                    activity: 0
                  cost: 40
                meta:
                  has_attachments: false
                  archived: false
                  isPastEventAndLocked: false
                  isSharedAndParentPortalSettingsLocked: false
                  event:
                    eventid: 1700001
                    name: "Winter Sleepover"
                    type: null
                    startdate: "2025-12-06"
                    enddate: "2025-12-07"
                    starttime: "11:00:00"
                    endtime: "14:00:00"
                    cost: 40
                    location: "Riverside Scout Hut"
                    latitude: "51.5007"
                    longitude: "-0.1246"
                    notes: ""
                    notepad: ""
                    publicnotes: "<h1>Winter Sleepover</h1><p>Bring a sleeping bag.</p>"
                    config: null
                    sectionid: 45002
                    kit_list_collection_id: 4401
                    googlecalendar: null
                    archived: false
                    soft_deleted: false
                    confdate: "2025-11-30"
                    allowchanges: false
                    disablereminders: false
                    attendancelimit: 24
                    limitincludesleaders: false
                    allowbooking: true
                    attendancereminder: 0
                    approval_status: null
                    created_at: "2025-08-25 20:47:25"
                    created_by: 100001
                    members:
                      - eventid: 1700001
                        scoutid: 3100001
                        attending: "Yes"
                        payment: "Manual"
                        details: null
                        member:
                          scoutid: 3100001
                          firstname: "Jamie"
                          lastname: "River"
                          photo_guid: "00000000-0000-0000-0000-000000000000"
                          dob: "2015-04-22"
                          started: "2021-09-01"
                          created_date: "2021-09-01 18:30:00"
                          last_accessed: "2025-09-15 19:00:00"
                    payment_dates:
                      - paymentid: 900001
                        schemeid: 47001
                        date: "2025-11-19"
                        amount: "40.00"
                        name: "Winter Sleepover"
                        archived: 0
                        description: null
                        order_id: null
                    activities: [ ]
                    kit_list_collection:
                      id: 4401
                      associated_type: "event"
                      section_id: 45002
                      name: "Sleepovers"
                      instructions: "Please label all kit."
                      created_at: "2023-09-27 20:59:35"
                      updated_at: "2023-09-27 20:59:35"
                  read_only: false
  /v3/events/event/{eventId}/members/attendance:
    get:
      summary: Get event attendance
      tags:
        - Events
      description: |
        One row per member in the section with their invitation/attendance
        state. `attending` is lowercase (`yes`, `no`, `invited`, or empty for
        not invited). `age_at_start` is `years / months`, or `25+` for adults.
        `meta.structure` lists any custom event columns.
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
        - name: term_id
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
      responses:
        "200":
          description: Event attendance.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - member_id: 3100001
                    patrol_id: 12001
                    first_name: "Jamie"
                    last_name: "River"
                    _filterString: "jamie river"
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    attending: "yes"
                    emailable: true
                    details: [ ]
                    age_at_start: "10 / 07"
                meta:
                  maximum_attendance_limit: 0
                  has_parent_portal: true
                  can_set_absent: false
                  structure: [ ]
                  rota_accepted: 0
                  structure_read_only: false
                  attendance_read_only: false
                  columns_read_only: false
                  approval_locked: false
                  shared_deadline_locked: false
                  approval_status: null
  /v3/events/event/{eventId}/members/payments:
    get:
      summary: Get event payments
      tags:
        - Events
      description: |
        Same rows as `members/attendance`, plus `payment` (`manual` or
        `automatic`) and `received_income` / `expected_income` in **pounds**.
        `meta.structure` lists one read-only column per payment scheme charge
        (`field` is `p<paymentid>`).
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
        - name: term_id
          in: query
          required: true
          schema:
            type: string
          description: Term ID.
      responses:
        "200":
          description: Event payments.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - member_id: 3100001
                    patrol_id: 12001
                    first_name: "Jamie"
                    last_name: "River"
                    _filterString: "jamie river"
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    attending: "yes"
                    emailable: true
                    payment: "automatic"
                    details: [ ]
                    received_income: 40
                    expected_income: 40
                meta:
                  maximum_attendance_limit: 0
                  has_parent_portal: true
                  can_set_absent: false
                  structure:
                    - name: "Winter Sleepover"
                      field: "p900001"
                      read_only: true
                  structure_read_only: false
                  attendance_read_only: false
                  columns_read_only: false
                  approval_locked: false
                  shared_deadline_locked: false
                  approval_status: null
                  has_payments: true
  /v3/events/event/{eventId}/parent_portal:
    get:
      summary: Get event Parent Portal settings
      tags:
        - Events
      description: |
        Booking settings shown to parents: confirmation deadline, whether
        changes/bookings are allowed, reminder settings, the linked kit list
        collection, and the map location. `meta.kit_list_collections` lists the
        section's collections (name suffixed with the item count).
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
      responses:
        "200":
          description: Parent Portal settings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  start_date: "2025-12-06"
                  confirmation_deadline: "2025-11-30"
                  cost: 40
                  allow_changes_before_deadline: false
                  allow_booking_when_show_in_portal: true
                  disable_invitation_reminders: false
                  send_attendance_reminders: 0
                  kit_list_collection_id: 4401
                  latitude: "51.5007"
                  longitude: "-0.1246"
                meta:
                  read_only: false
                  past_locked: false
                  shared_locked: false
                  confirmation_deadline_locked: false
                  has_parent_portal: true
                  currency: "GBP"
                  kit_list_collections:
                    - id: 4401
                      name: "Sleepovers (12)"
  /v3/events/event/{eventId}/timetable:
    get:
      summary: Get event timetable
      tags:
        - Events
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
      responses:
        "200":
          description: Event timetable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  start_date: "2025-12-06"
                  end_date: "2025-12-07"
                  start_time: "11:00:00"
                  end_time: "14:00:00"
                  entries: [ ]
                meta:
                  read_only: false
                  past_locked: false
                  shared_locked: false
                  details_locked: false
  /v3/events/event/{eventId}/activities:
    get:
      summary: Get event "at home" activities
      tags:
        - Events
      description: |
        Activities attached to the event (the "at home" / remote activity
        feature). `meta.extra` mirrors the event's `extra` settings.
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
      responses:
        "200":
          description: Event activities.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta:
                  extra:
                    at_home:
                      enabled: false
                      introduction: ""
                      parent_uploads_enabled: false
                      chat_enabled: false
                      activities: [ ]
                    sharing:
                      sharee: false
                      deleted: false
  /v3/kit/{sectionid}/event:
    get:
      summary: Get kit list items and collections
      tags:
        - Events
      description: |
        Kit lists ("what to bring") for events. `data` is the catalogue of kit
        items (OSM-wide items have `section_id: 0`), grouped by `heading`;
        `meta.collections` are the section's named kit lists.
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      responses:
        "200":
          description: Kit items and collections.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 39
                    associated_type: "event"
                    section_id: 0
                    heading: "Activities"
                    name: "Bike"
                    description: "Check brakes and gears work."
                    purchase_link: ""
                    multiple: false
                    created_at: "2022-07-05 13:11:40"
                    updated_at: "2022-07-05 13:11:40"
                    collection_items: [ ]
                meta:
                  collections:
                    - id: 4401
                      associated_type: "event"
                      section_id: 45002
                      name: "Sleepovers"
                      instructions: "Please label all kit."
                      created_at: "2023-09-27 20:59:35"
                      updated_at: "2023-09-27 20:59:35"
  /v3/kit/{sectionid}/event/{collectionId}:
    post:
      summary: Update a kit list collection
      tags:
        - Events
      description: |
        Saves a kit list collection. `items` is a JSON array of
        `{"item_id": <kit item id>, "quantity": <n>}` and **replaces** the
        collection's contents.
      parameters:
        - name: sectionid
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
        - name: collectionId
          in: path
          required: true
          schema:
            type: string
          description: Kit list collection ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                name:
                  type: string
                instructions:
                  type: string
                items:
                  type: string
                  description: JSON array of `{item_id, quantity}`.
            example:
              name: "Sleepovers"
              instructions: "Please label all kit."
              items: "[{\"item_id\":39,\"quantity\":1},{\"item_id\":40,\"quantity\":1}]"
      responses:
        "200":
          description: Collection saved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta: [ ]
  /v3/finances/accounting/financial_year/section/{sectionid}:
    get:
      summary: List financial years
      tags:
        - Finance
      description: |
        The section's financial years, newest first, each with income/expense
        totals. Use the `id` with the cashbook, budget and annual report
        endpoints. **All accounting amounts are integers in pence.**
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Financial years.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 7002
                    section_id: 45003
                    start_date: "2025-04-06"
                    end_date: "2026-04-05"
                    created_at: "2025-04-06 09:00:00"
                    updated_at: "2025-04-06 09:00:00"
                    title: "2025 - 2026"
                    transaction_summary:
                      total_income: 250000
                      total_expense: 180000
                meta: [ ]
  /v3/finances/accounting/financial_year/cashbook/transactions/year/{financialYearId}:
    get:
      summary: List cashbook transactions for a financial year
      tags:
        - Finance
      description: |
        Paginated cashbook. Each transaction carries its category split
        (`categories[]`, each with an `amount` and the resolved `category`),
        and is linked to the imported bank line via `bank_account_transaction_id`.
        `meta` includes year totals, the full category list
        (`categories`), the categories used this year
        (`categories_in_financial_year`), and tracked `items`.

        Amounts are in **pence**. Payment-provider payouts are typically split
        into an income category plus a `Bank Fees` expense category, so
        `categories[].amount` may not sum to the headline `amount`.
      parameters:
        - name: financialYearId
          in: path
          required: true
          schema:
            type: string
          description: Financial year ID.
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          required: false
          schema:
            type: integer
            default: 25
      responses:
        "200":
          description: Cashbook page.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 800001
                    incrementing_id: 101
                    section_id: 45003
                    date: "2025-10-08"
                    description: "PAYOUT REF EXAMPLE-0001"
                    description_sanitised: "PAYOUT REF EXAMPLE"
                    bank_account_reference_sanitised: "PAYOUT REF EXAMPLE"
                    bank_account_transaction_id: 900001
                    type: "income"
                    amount: 9706
                    locked: false
                    categorised: 9706
                    number_categories: 2
                    auto_categorisation: "no guess"
                    created_at: "2025-10-10 16:24:16"
                    updated_at: "2025-10-10 16:24:16"
                    categories:
                      - id: 810001
                        category_id: 3
                        item_id: 0
                        transaction_id: 800001
                        amount: 10000
                        category:
                          id: 3
                          section_id: 0
                          name: "Membership Fees"
                          type: "income"
                          icon: "membership"
                        item: null
                      - id: 810002
                        category_id: 30
                        item_id: 0
                        transaction_id: 800001
                        amount: 294
                        category:
                          id: 30
                          section_id: 0
                          name: "Bank Fees"
                          type: "expense"
                          icon: "bank"
                        item: null
                    attachments: 0
                meta:
                  number_transactions: 1
                  total_expense: 294
                  total_income: 10000
                  total_amount: 0
                  categories:
                    - id: 55
                      section_id: 0
                      name: "Admin"
                      type: "expense"
                      icon: "administration"
                      usages: [ ]
                  categories_in_financial_year: [ ]
                  items: [ ]
                  financial_year:
                    start_date: "2025-04-06"
                    end_date: "2026-04-05"
  /v3/finances/accounting/financial_year/cashbook/transactions/section/{sectionid}:
    post:
      summary: Create a cashbook transaction
      tags:
        - Finance
      description: |
        Creates a cashbook entry, usually from an imported bank line
        (`bank_account_transaction_id`). `amount` is in **pence**. `categories`
        is a JSON array splitting the amount across categories:

        | Field | Notes |
        |-------|-------|
        | `category_id` | Category ID (see the `categories` list in cashbook `meta`). |
        | `item_id` | Tracked item, or `0`. |
        | `amount` | Pence. |
        | `description`, `payment_id`, `scheme_id`, `is_fee` | Present when the split comes from an online payment payout. |

        `attachments_id` is a client-generated GUID used to upload receipts via
        the cashbook uploads manifest before the transaction exists.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [ description, type, date, amount, categories ]
              properties:
                description:
                  type: string
                type:
                  type: string
                  enum: [ income, expense ]
                date:
                  type: string
                  format: date
                amount:
                  type: integer
                  description: Pence.
                categories:
                  type: string
                  description: JSON array of category splits.
                attachments_id:
                  type: string
                  format: uuid
                bank_account_transaction_id:
                  type: string
            example:
              description: "PAYOUT REF EXAMPLE-0002"
              type: income
              date: "2025-04-29"
              amount: 2411
              categories: "[{\"category_id\":3,\"item_id\":0,\"amount\":2500},{\"category_id\":30,\"item_id\":0,\"amount\":89,\"is_fee\":true}]"
              attachments_id: "00000000-0000-0000-0000-000000000002"
              bank_account_transaction_id: "900002"
      responses:
        "200":
          description: Created transaction, with the category OSM would have guessed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  id: 800002
                  guessed_category_id: 3
                  guessed_item_id: 0
                meta: [ ]
  /v3/finances/accounting/financial_year/cashbook/transactions/{transactionId}:
    post:
      summary: Update a cashbook transaction
      tags:
        - Finance
      description: |
        Same body as create. When editing existing splits, each entry in
        `categories` includes its `id` (and the other fields returned by the
        cashbook list).
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
          description: Cashbook transaction ID.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
            example:
              description: "PAYOUT REF EXAMPLE-0001"
              type: income
              date: "2025-10-08"
              amount: 9706
              categories: "[{\"id\":810001,\"category_id\":3,\"item_id\":0,\"amount\":10000},{\"id\":810002,\"category_id\":30,\"item_id\":0,\"amount\":294}]"
      responses:
        "200":
          description: Transaction updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta: [ ]
  /v3/finances/accounting/financial_year/budgets/year/{financialYearId}:
    get:
      summary: Get budgets for a financial year
      tags:
        - Finance
      description: Every category with its budgeted `amount` (pence) for the year.
      parameters:
        - name: financialYearId
          in: path
          required: true
          schema:
            type: string
          description: Financial year ID.
      responses:
        "200":
          description: Budgets by category.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 55
                    section_id: 0
                    name: "Admin"
                    type: "expense"
                    icon: "administration"
                    budgets: [ ]
                    amount: 20000
                meta:
                  financial_year:
                    id: 7002
                    section_id: 45003
                    start_date: "2025-04-06"
                    end_date: "2026-04-05"
  /v3/finances/accounting/financial_year/annual_report/{financialYearId}:
    get:
      summary: Get annual report
      tags:
        - Finance
      description: |
        Income and expense by category, balances per bank account, and a
        summary (balance brought forward, carried forward, …) for the year,
        optionally side-by-side with another year (`other_financial_year_id`,
        `0` for none). Amounts in pence.
      parameters:
        - name: financialYearId
          in: path
          required: true
          schema:
            type: string
          description: Financial year ID.
        - name: other_financial_year_id
          in: query
          required: false
          schema:
            type: string
          description: Comparison year ID, or `0`.
      responses:
        "200":
          description: Annual report.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  expense:
                    - id: 55
                      name: "Admin"
                      this_year_amount: 11750
                      other_year_amount: 0
                  income:
                    - id: 3
                      name: "Membership Fees"
                      this_year_amount: 250000
                      other_year_amount: 230000
                  bank_accounts:
                    - id: 5001
                      name: "Main Account"
                      this_year_amount: 400000
                      other_year_amount: 350000
                  summary:
                    - id: 0
                      name: "Account balance brought forward"
                      this_year_amount: 350000
                      other_year_amount: 300000
                    - id: 1
                      name: "Account balance carried forward"
                      this_year_amount: 400000
                      other_year_amount: 350000
                meta:
                  financial_year:
                    id: 7002
                    section_id: 45003
                    start_date: "2025-04-06"
                    end_date: "2026-04-05"
                  other_financial_years: [ ]
                  other_financial_year_id: 0
                  bank_accounts_have_invalid_opening_balance: false
  /v3/finances/accounting/reports/overview/{sectionid}:
    get:
      summary: Get monthly finance overview
      tags:
        - Finance
      description: Rolling 12 months keyed by `yyyy-mm`, with `income`, `expense` and running `total` balance (pence).
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Monthly overview.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  "2025-09":
                    income: 50000
                    expense: 11750
                    total: 388000
                  "2025-10":
                    income: 10000
                    expense: 0
                    total: 403000
                meta: [ ]
  /v3/finances/accounting/bank_accounts/section/{sectionid}:
    get:
      summary: List bank accounts
      tags:
        - Finance
      description: |
        The section's bank accounts with opening/current balances (pence) and
        the last CSV import column mapping. An account with a non-null
        `expense_account` is the OSM expense-card account (its value is the
        payment provider's connected-account ID).
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Bank accounts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 5001
                    section_id: 45003
                    name: "Main Account"
                    opening_balance: 300000
                    opening_date: "2021-02-05"
                    current_balance: 403000
                    config:
                      last_import_mapping:
                        headers: [ "Number", "Date", "Account", "Amount", "Subcategory", "Memo" ]
                        column_count: 6
                        csv_layout:
                          - column_number: 0
                            column_letter: "A"
                            heading: "Number"
                            target_field: "do_not_import"
                          - column_number: 1
                            column_letter: "B"
                            heading: "Date"
                            target_field: "date"
                        column_letters: "ABCDEF"
                    expense_account: null
                    created_at: "2023-12-10 17:06:23"
                    updated_at: "2024-02-06 15:05:53"
                    deleted_at: null
                    updated_at_local_date_time: "06/02/2024 15:05"
                    first_transaction_date: "2021-02-05"
                    last_transaction_date: "2025-10-05"
                    number_unprocessed_transactions: 0
                  - id: 5002
                    section_id: 45003
                    name: "Expense Account"
                    opening_balance: 0
                    opening_date: "2023-11-29"
                    current_balance: 0
                    config: null
                    expense_account: "acct_EXAMPLE0000000000"
                    created_at: "2023-11-29 16:20:55"
                    updated_at: "2023-11-29 16:20:55"
                    deleted_at: null
                    updated_at_local_date_time: "29/11/2023 16:20"
                    first_transaction_date: null
                    last_transaction_date: null
                    number_unprocessed_transactions: 0
                meta:
                  internal_transfer_difference: 0
  /v3/finances/accounting/bank_accounts/{bankAccountId}/transactions:
    get:
      summary: List imported bank transactions
      tags:
        - Finance
      description: |
        Imported bank statement lines for an account. `mode=unprocessed`
        returns lines not yet turned into cashbook entries. `meta` carries the
        category and item lists for categorising them.
      parameters:
        - name: bankAccountId
          in: path
          required: true
          schema:
            type: string
          description: Bank account ID.
        - name: mode
          in: query
          required: false
          schema:
            type: string
          description: Observed `unprocessed`.
        - name: expense_cardholder_id
          in: query
          required: false
          schema:
            type: string
          description: Filter to one expense cardholder, or `0` for all.
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          required: false
          schema:
            type: integer
            default: 25
      responses:
        "200":
          description: Bank transactions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta:
                  number_transactions: 0
                  categories:
                    - id: 45
                      section_id: 0
                      name: "Activity Equipment"
                      type: "expense"
                      icon: "activity"
                      usages: [ ]
                  items:
                    - id: 4001
                      section_id: 45003
                      name: "Minibus"
                      associated_type: null
                      associated_section_id: 0
                      associated_id: 0
                      categories: [ 55 ]
                      archived_at: null
                      deleted_at: null
  /v3/finances/accounting/bank_account_transactions/{bankAccountTransactionId}/guess_category:
    get:
      summary: Guess a category for a bank transaction
      tags:
        - Finance
      description: |
        Returns OSM's suggested `category_id` / `item_id` for an imported bank
        line (`0` if no guess), any payout breakdown, and the
        `next_bank_transaction` to process — used to step through
        uncategorised lines one at a time.
      parameters:
        - name: bankAccountTransactionId
          in: path
          required: true
          schema:
            type: string
          description: Imported bank transaction ID.
      responses:
        "200":
          description: Category guess.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  category_id: 33
                  item_id: 0
                  payout_details: [ ]
                  next_bank_transaction:
                    id: 900003
                    bank_account_id: 5001
                    card_id: 0
                    cardholder_id: 0
                    transaction_id: 0
                    reference: "EXAMPLE PAYEE REF"
                    description: null
                    amount: 2500
                    date: "2025-04-28"
                    is_transfer: false
                    created_at: "2025-10-10 16:23:53"
                    updated_at: "2025-10-10 16:23:53"
                meta: [ ]
  /v3/finances/accounting/bank_accounts/{bankAccountId}/trustees:
    get:
      summary: List expense account trustees
      tags:
        - Finance
      description: |
        The people registered against the expense-card account with the
        payment provider (KYC). Each person has their role flags
        (`director`, `executive`, `representative`, `account_opener`, …),
        outstanding `requirements`, and identity `verification` status.
        **Contains personal data of adult volunteers.**
      parameters:
        - name: bankAccountId
          in: path
          required: true
          schema:
            type: string
          description: Bank account ID (the expense account).
      responses:
        "200":
          description: Trustees.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  people:
                    - id: 20001
                      account_identifier: "acct_EXAMPLE0000000000"
                      person_identifier: "person_EXAMPLE0000000001"
                      first_name: "Alex"
                      last_name: "Scout"
                      email: "leader@example.org"
                      relationship:
                        authorizer: false
                        director: true
                        executive: true
                        legal_guardian: false
                        owner: false
                        percent_ownership: null
                        representative: true
                        title: "Group Lead Volunteer"
                        account_opener: true
                      requirements:
                        alternatives: [ ]
                        currently_due: [ ]
                        errors: [ ]
                        eventually_due: [ ]
                        past_due: [ ]
                        pending_verification: [ ]
                      verification:
                        additional_document:
                          back: null
                          details: null
                          details_code: null
                          front: null
                        details: null
                        details_code: null
                        document:
                          back: null
                          details: null
                          details_code: null
                          front: null
                        status: "verified"
                      created_at: "2023-11-29 16:23:01"
                      updated_at: "2025-10-08 01:35:57"
                meta: [ ]
  /v3/finances/accounting/categories/section/{sectionid}:
    get:
      summary: List accounting categories
      tags:
        - Finance
      description: |
        Income and expense categories used to classify cashbook transactions
        and budget lines. Each has a `type` (`income` or `expense`) and an
        `icon` name. Soft-deleted categories carry a `deleted_at` timestamp;
        `meta.has_deleted_categories` says whether any exist.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Categories.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 81001
                    section_id: 45001
                    name: "Activities"
                    type: "expense"
                    icon: "activity"
                    created_at: "2023-09-01 19:00:00"
                    updated_at: "2023-09-01 19:00:00"
                    deleted_at: null
                    visibilities: [ ]
                  - id: 81002
                    section_id: 45001
                    name: "Subscriptions"
                    type: "income"
                    icon: "money"
                    created_at: "2023-09-01 19:00:00"
                    updated_at: "2023-09-01 19:00:00"
                    deleted_at: null
                    visibilities: [ ]
                meta:
                  has_deleted_categories: false
  /v3/finances/accounting/{sectionid}/existing_group_accounts:
    get:
      summary: List the group's existing accounts
      tags:
        - Finance
      description: |
        Bank accounts that already exist elsewhere in the section's group,
        split into `expense` (expense-card accounts) and `standard` accounts.
        Used when linking a section to an account instead of creating a new one.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Existing accounts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  expense:
                    - id: 82001
                      name: "Group Expense Account"
                  standard: [ ]
                meta:
                  has_visible_categories: true
  /v3/comments/accounting/{sectionid}/list:
    get:
      summary: List accounting comments
      tags:
        - Finance
      description: |
        Comment thread for the section's accounts. `meta` holds the current
        user's comment permissions (`can_add`, `can_edit_self`,
        `can_delete_others`, …) and `notification_topics`, keyed by topic
        (e.g. `comments/accounting/45001`), which describes how the client
        opens the thread from a notification. The path segment is the section
        ID, but `section_id` is also required in the query string. Only an
        empty `data` list has been observed.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
        - name: section_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Comments and permissions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta:
                  can_edit_self: true
                  can_edit_others: false
                  can_delete_self: true
                  can_delete_others: false
                  can_add: true
                  notification_topics:
                    comments/accounting/45001:
                      label: "Accounts"
                      section_id: 45001
                      permission_type: "finance"
                      permission_value: "10"
                      specified_section_id: 45001
                      javascript_data:
                        require: "accounting"
                        execute: "open"
                        args: [ "comments" ]
                        params:
                          id: "45001"
                          react: "Accounting"
                          props:
                            selected_tab: "comments"
                  has_uploads: false
  /ext/finances/onlinepayments/:
    get:
      summary: Get Online Payments schemes
      tags:
        - Finance
      description: |
        Online Payments schemes for the section, with the linked
        `bank_accounts` and a `config` block describing the section's
        subscription (`subscription_level`, `subscription_active`, expiry
        dates) and paid add-ons (`portal`, `portalExpires`). `items` is empty
        when no schemes are set up; populated items have not been observed.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getSchemes ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Schemes and subscription config.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
              example:
                identifier: "schemeid"
                label: "Payment schemes"
                has_accounts: false
                items: [ ]
                bank_accounts: [ ]
                config:
                  subscription_level: 3
                  subscription_expires: "2026-09-01"
                  sectionType: "cubs"
                  section_type: "cubs"
                  parentSectionId: "44001"
                  hierarchy_lowest_level: true
                  hierarchy_operating_level: "section"
                  config_subscriptions_checked: "2026-11-01 09:00:00"
                  subscription_active: true
                  has_expense_account: true
                  portal:
                    expense_cards_fees: 1
                    accounts: 1
                    filestorage: 1
                  portalExpires:
                    accountsA: 1
                    accounts: "2026-09-01"
                    filestorage: "2026-09-01"
                    filestorageA: 1
  /ext/finances/onlinepayments/merchants/:
    get:
      summary: Get Online Payments merchant summary
      tags:
        - Finance
      description: Payment-provider merchant accounts linked to the section. `items` is empty when none are connected; populated items have not been observed.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getSummary ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Merchant summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
              example:
                items: [ ]
  /v3/finances/accounting/expense_cardholders/section/{sectionid}:
    get:
      summary: List expense cardholders
      tags:
        - Finance
      description: |
        Expense cardholders for the section, each with spend limits (pence),
        allowed `spending_categories`, their cards (`last4`, expiry, status),
        the user, and the linked expense bank account. `meta` reports the
        card-issuing status and the category list.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Expense cardholders.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 6001
                    cardholder_identifier: "ich_EXAMPLE0000000001"
                    status: "active"
                    user_id: 100001
                    section_id: 45003
                    bank_account_id: 5002
                    total_amount_spent: 0
                    total_spend_limit: 50000
                    new_card_available: false
                    spending_categories: [ 45, 55 ]
                    requirements:
                      disabled_reason: null
                      past_due: [ ]
                    deleted: false
                    created_at: "2025-10-09 12:57:51"
                    created_by: 100001
                    updated_at: "2025-10-10 14:26:19"
                    daily_spend_limit: 0
                    weekly_spend_limit: 0
                    monthly_spend_limit: 0
                    uncategorised_transactions: 0
                    periodic_limits: null
                    cards:
                      - id: 6101
                        card_identifier: "ic_EXAMPLE0000000001"
                        status: "inactive"
                        cardholder_id: 6001
                        last4: "0000"
                        exp_month: 1
                        exp_year: 2030
                        has_been_activated: false
                        created_at: "2025-10-10 14:26:17"
                        updated_at: "2025-10-10 14:26:17"
                    user:
                      photo_url: false
                      full_name: "Alex Scout"
                    bank_account:
                      id: 5002
                      section_id: 45003
                      name: "Expense Account"
                      current_balance: 0
                      expense_account: "acct_EXAMPLE0000000000"
                meta:
                  unallocated_balance: 0
                  has_expense_account: true
                  requirements_due: [ ]
                  requirements_deadline: null
                  future_requirements_due: [ ]
                  future_requirements_deadline: null
                  bank_account_id: 5002
                  issuing_status: "active"
                  balance: 0
                  categories: [ ]
                  failed_payouts: [ ]
                  pending_payout_change: null
                  card_issuing_disabled_reason: null
                  card_issuing_status: "active"
                  account_rejected_reason: null
                  account_rejected_details: [ ]
  /v3/finances/accounting/items/section/{sectionid}:
    get:
      summary: List tracked finance items
      tags:
        - Finance
      description: |
        "Items" are things you track spend against across categories (e.g. a
        minibus, or an event via `associated_type`). Each item reports its
        income/expense (pence) within the given financial year.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
        - name: financial_year_id
          in: query
          required: false
          schema:
            type: string
          description: Financial year ID.
      responses:
        "200":
          description: Items.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - id: 4001
                    section_id: 45003
                    name: "Minibus"
                    associated_type: null
                    associated_section_id: 0
                    associated_id: 0
                    categories: [ 55 ]
                    archived_at: null
                    deleted_at: null
                    income_expense:
                      income: 0
                      expense: 11750
                      has_transactions: true
                    associated_name: null
                    categories_in_use: [ 55 ]
                    associated_section: null
                meta:
                  categories:
                    "55": "Admin"
                  all_items: [ ]
                  all_categories: [ ]
  /v3/finances/accounting/items/links/event/{eventId}:
    get:
      summary: List cashbook lines linked to an event
      tags:
        - Finance
      parameters:
        - $ref: "#/components/parameters/EventIdPath"
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          required: false
          schema:
            type: integer
            default: 25
      responses:
        "200":
          description: Linked transactions with totals (pence).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta:
                  number_transactions: 0
                  total_income: 0
                  total_expense: 0
                  total_amount: 0
  /ext/finances/invoices/summary/:
    get:
      summary: List invoices
      tags:
        - Finance
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getInvoiceSummary ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
        - name: termid
          in: query
          required: true
          schema:
            type: string
        - name: is_dgrid
          in: query
          required: false
          schema:
            type: string
            enum: [ "true" ]
      responses:
        "200":
          description: Dojo item store of invoices.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ItemStore"
              example:
                identifier: invoiceid
                label: name
                items: [ ]
  /ext/finances/accounting/financial_year/cashbook/transactions/uploads/:
    get:
      summary: Cashbook receipt attachments manifest
      tags:
        - Files
      description: |
        File manifest for receipts attached to a cashbook transaction. With
        `temp=true`, `id` is the client-generated `attachments_id` GUID used
        before the transaction is created. See [Files](#tag/Files).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Transaction ID, or the `attachments_id` GUID when `temp=true`.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: path
          in: query
          required: false
          schema:
            type: string
        - name: temp
          in: query
          required: false
          schema:
            type: string
            enum: [ "true", "false" ]
        - name: bank_account_transaction_id
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/FileManifest"
    post:
      summary: Cashbook receipt attachments manifest (POST)
      tags:
        - Files
      description: |
        The web client also fetches the cashbook manifest with a POST. All
        parameters, including `action=manifest`, are in the query string and
        there is no body. The response matches the GET form.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Transaction ID, or the `attachments_id` GUID when `temp=true`.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: path
          in: query
          required: false
          schema:
            type: string
        - name: temp
          in: query
          required: false
          schema:
            type: string
            enum: [ "true", "false" ]
        - name: bank_account_transaction_id
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/FileManifest"
  /ext/finances/accounting/financial_year/cashbook/transactions/uploads/index.php:
    get:
      summary: Preview a cashbook receipt
      tags:
        - Files
      description: |
        Image preview of an attached file, served as `image/png` (or the format
        in `fm`). Use the `preview` URLs from the manifest
        (`data.manifest[*].preview` / `preview_sizes`) as they are: `s` is a
        server-generated signature over the other parameters, so changing
        `w`, `h` or `fit` yourself will not work. Other features' `uploads/`
        endpoints follow the same pattern.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ preview ]
        - name: file
          in: query
          required: true
          schema:
            type: string
          description: File path within the manifest.
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Transaction ID.
        - name: bank_account_transaction_id
          in: query
          required: false
          schema:
            type: string
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: cb
          in: query
          required: false
          schema:
            type: string
          description: Cache-buster.
        - name: _bypass_service_worker
          in: query
          required: false
          schema:
            type: string
            enum: [ "1" ]
        - name: fm
          in: query
          required: false
          schema:
            type: string
          description: Output format (observed `png`).
        - name: fit
          in: query
          required: false
          schema:
            type: string
          description: Resize mode (observed `crop`).
        - name: w
          in: query
          required: false
          schema:
            type: integer
        - name: h
          in: query
          required: false
          schema:
            type: integer
        - name: s
          in: query
          required: true
          schema:
            type: string
          description: Signature generated by OSM; copy it from the manifest.
      responses:
        "200":
          description: Preview image.
          content:
            image/png:
              schema:
                type: string
                format: binary
  /ext/finances/accounting/bank_accounts/uploads/:
    get:
      summary: Bank account attachments manifest
      tags:
        - Files
      description: |
        File manifest for documents attached to a bank account (e.g.
        statements). `manifest` is an empty array when there are no files.
        See [Files](#tag/Files).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Bank account ID.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: path
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/FileManifest"
  /ext/members/email/campaigns/:
    get:
      summary: List email campaign templates
      tags:
        - Email
      description: |
        Saved campaign (newsletter) templates. `campaign_preview` is a relative
        URL to an HTML preview. Load a campaign's content with
        `/v3/members/email/campaigns/snippets/{campaignId}`.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ listCampaigns ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaigns.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - campaign_id: 70001
                    title: "Termly Newsletter"
                    template: "turquoise"
                    created_at: "2024-12-19 21:10:35"
                    last_used: "2024-12-20 20:46:18"
                    updated_at: "2024-12-19 21:10:42"
                    campaign_preview: "/ext/members/email/campaigns/?action=preview&campaign_id=70001"
                    has_scheduled_email: false
                meta: [ ]
  /v3/members/email/campaigns/snippets/{campaignId}:
    get:
      summary: Get a campaign's content snippets
      tags:
        - Email
      description: |
        The ordered content blocks (`snippets`) of a campaign template, the
        HTML `snippet_templates` used to render each block type
        (`fullwidth_text`, `text_text`, `splitter`), and the template's `body`
        and `style`. When sending, each `snippet_id` becomes a region key in
        `sendTemplate`'s `edits`. Fetching a campaign for editing is preceded
        by `POST /ext/generic/locks/?action=obtain` with `item=campaign_<id>`.
      parameters:
        - name: campaignId
          in: path
          required: true
          schema:
            type: string
          description: Campaign ID.
      responses:
        "200":
          description: Campaign snippets.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  snippets:
                    - snippet_id: 300001
                      snippet: "fullwidth_text"
                      contents:
                        - text: "<h1>Termly Newsletter</h1>"
                      config: null
                      order: 1
                  snippet_templates:
                    fullwidth_text: "<tr id=\"s___snippet_id[0]\">…</tr>"
                    text_text: "__css__\n<tr id=\"s___snippet_id[0]\">…</tr>"
                    splitter: "<tr id=\"s___snippet_id[0]\">…</tr>"
                  campaign_template: "turquoise"
                  body: "<div class=\"body\">…</div>"
                  style: ".template_editor_turquoise { … }"
                meta: [ ]
  /v3/members/email/compose/{sectionid}:
    get:
      summary: Get members for the email composer
      tags:
        - Email
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
        - name: term_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Members available as recipients.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - member_id: 3100001
                    firstname: "Jamie"
                    lastname: "River"
                    patrol_id: 12001
                    patrol: "Blue"
                meta: [ ]
  /v3/members/email/draft/{sectionid}:
    get:
      summary: List draft emails
      tags:
        - Email
      description: |
        The current user's draft emails for the section. `edits` is the decoded
        region array (the same structure sent to `sendTemplate`).
        `meta.template_count` is the number of saved campaigns.
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Drafts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - emailid: 80000001
                    guid: "00000000-0000-0000-0000-000000000001"
                    status: "DRAFT"
                    sectionid: 45002
                    emailtype: "MANUAL"
                    subject: ""
                    sendtime: "2025-09-22 20:52:04"
                    content: null
                    edits:
                      - - text: "<p>Draft text</p>"
                    campaign_id: 0
                    to: null
                    user_id: 100001
                    from_email: ""
                    full_from_email: ""
                    config: null
                meta:
                  template_count: 3
  /v3/members/email/draft:
    post:
      summary: Save a draft email
      tags:
        - Email
      description: |
        Autosaves the email being composed. Called repeatedly while editing;
        the `guid` identifies the draft so repeated saves update the same
        record. Returns the draft's ID, which is then passed to `sendTemplate`
        as `draft_email_id`.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                subject:
                  type: string
                guid:
                  type: string
                  format: uuid
                section_id:
                  type: string
                campaign_id:
                  type: string
                edits:
                  type: string
                  description: JSON object of template regions (see `sendTemplate`).
            example:
              subject: "Weekly Meeting Reminder"
              guid: "00000000-0000-0000-0000-000000000001"
              section_id: "45002"
              campaign_id: "0"
              edits: "{\"0\":{\"0\":{\"text\":\"<p>Draft text</p>\"}}}"
      responses:
        "200":
          description: Draft saved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  id: 80000001
                meta: [ ]
  /ext/members/sms/:
    get:
      summary: Get SMS delivery report
      tags:
        - Email
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getDeliveryReport ]
        - name: sectionid
          in: query
          required: true
          schema:
            type: string
        - name: dateFormat
          in: query
          required: false
          schema:
            type: string
            enum: [ uk ]
      responses:
        "200":
          description: Dojo item store of sent SMS messages.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ItemStore"
              example:
                identifier: smsid
                items: [ ]
  /ext/settings/parents/loginhistory/:
    get:
      summary: Get Parent Portal login history
      tags:
        - Members
      description: |
        One row per member showing the linked Parent Portal account (if any),
        how often it has been used, and password / failed-login recency
        (`never`, `over_30_days`, …). `id` is `<member_id>-<user_id>` (the
        user part is empty when no account is linked). **Contains parents'
        names and email addresses.**
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getLoginHistoryMyMember ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Login history.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - member_id: 3100001
                    first_name: "Jamie"
                    last_name: "River"
                    photo_guid: "00000000-0000-0000-0000-000000000000"
                    patrol_id: 12001
                    access_count: 12
                    date_last_accessed: "2025-05-12 16:27:39"
                    user_id: 200101
                    access_type: "member"
                    user_full_name: "Morwenna River"
                    user_pic: null
                    user_email: "parent@example.org"
                    password_last_updated: "over_30_days"
                    id: "3100001-200101"
                    last_failed_login: "never"
                    _filterString: "jamie river morwenna river parent@example.org"
                    show_unlock: false
                  - member_id: 3100002
                    first_name: "Riley"
                    last_name: "Park"
                    photo_guid: null
                    patrol_id: 12002
                    access_count: 0
                    date_last_accessed: ""
                    user_id: 0
                    access_type: null
                    user_full_name: null
                    user_pic: null
                    user_email: null
                    password_last_updated: "never"
                    id: "3100002-"
                    last_failed_login: "never"
                    _filterString: "riley park "
                    show_unlock: false
                meta:
                  itemstore:
                    identifier: id
  /ext/programme/activities/:
    get:
      summary: Activity library
      tags:
        - Programme
      description: |
        OSM's shared activity library.

        - `getActivity` — one activity (`id` + `version`) with its full
          details, attached `files`, available `versions`, suitable `sections`,
          and whether the current user owns it.
        - `searchByNewActivities` — the newest shared activities (up to 100
          `items`; `num` is the total, `query` echoes `newest activities`).
        - `getTags` — every activity tag in use OSM-wide (a very large list —
          over ten thousand entries).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getActivity, searchByNewActivities, getTags ]
        - name: id
          in: query
          required: false
          schema:
            type: string
          description: Activity ID (`getActivity`).
        - name: version
          in: query
          required: false
          schema:
            type: string
          description: Activity version (`getActivity`).
        - name: activity_unique_id
          in: query
          required: false
          schema:
            type: string
          description: "`getActivity` — observed `0`."
        - name: section
          in: query
          required: false
          schema:
            type: string
          description: Section type.
        - name: sectionid
          in: query
          required: false
          schema:
            type: string
          description: Section ID (`getActivity`).
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Section ID (`searchByNewActivities`).
      responses:
        "200":
          description: Activity, search results, or tags.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
              examples:
                getActivity:
                  summary: One activity (action=getActivity)
                  value:
                    details:
                      id: "500001"
                      activityid: "50001"
                      version: "0"
                      userid: "100002"
                      site_type: "scouts"
                      user_name: "Sam Example"
                      title: "Knot Relay"
                      description: "Teams race to tie a set of knots."
                      resources: "Rope lengths"
                      instructions: "Split into teams…"
                      runningtime: "15"
                      location: "both"
                      shared: "2"
                      visibility: "all"
                      rating: 5
                      updated_at: "2025-10-08 21:27:17"
                      generic: "0"
                      created_at: "2025-10-08 21:27:17"
                      updatetime: "2025-10-08 21:27:17"
                      icons: [ "runningtime-15", "inandoutdoors" ]
                      tags: [ ]
                      review_summary:
                        writeup:
                          rating: 0
                          number_reviews: 0
                        activity:
                          rating: 0
                          number_reviews: 0
                    files:
                      - fileid: 600001
                        activityid: "50001"
                        filename: "knot-relay.pdf"
                        type: "application/pdf"
                        icon: "page_white_acrobat"
                        preview: "/ext/programme/activities/attachments/files/?action=preview&…"
                        from: "uploads"
                        name: "knot-relay.pdf"
                    thumb: false
                    rating: 5
                    used: 0
                    similar: [ ]
                    sections: [ "beavers", "cubs", "scouts", "explorers" ]
                    versions:
                      - value: "0"
                        user_name: "Sam Example"
                        updatetime: "2025-10-08 21:27:17"
                        selected: true
                        current: true
                    tags: [ ]
                    is_owner: false
                    can_delete: false
                searchByNewActivities:
                  summary: Newest activities (action=searchByNewActivities)
                  value:
                    items:
                      - activityid: "50001"
                        title: "Knot Relay"
                        description: "Teams race to tie a set of knots."
                        runningtime: "15"
                        location: "both"
                        shared: "2"
                        visibility: "all"
                        site_type: "scouts"
                        rating: 5
                        icons: [ "runningtime-15", "inandoutdoors" ]
                        tags: [ ]
                        review_summary:
                          writeup:
                            rating: 0
                            number_reviews: 0
                          activity:
                            rating: 0
                            number_reviews: 0
                    num: 1
                    query: "newest activities"
                getTags:
                  summary: All activity tags (action=getTags)
                  value:
                    identifier: name
                    label: name
                    items:
                      - name: "camp"
                      - name: "knots"
  /ext/programme/activities/attachments/files/:
    get:
      summary: Activity attachments manifest
      tags:
        - Files
      description: |
        File manifest for an activity's attachments. Unlike other manifests,
        this one is populated: each entry in `manifest` is keyed by path and
        has the file's `mime`, `bytes`, human `size`, upload/access dates,
        `access_count`, and `preview` URLs. See [Files](#tag/Files).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Activity ID.
        - name: activity_id
          in: query
          required: true
          schema:
            type: string
          description: Activity ID.
        - name: activity_unique_id
          in: query
          required: false
          schema:
            type: string
          description: The activity version's unique ID (`details.id` from `getActivity`).
        - name: section_id
          in: query
          required: false
          schema:
            type: string
          description: Observed `0`.
        - name: path
          in: query
          required: false
          schema:
            type: string
        - name: temp
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Activity file manifest.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileManifestResponse"
              example:
                status: true
                error: null
                data:
                  manifest:
                    "/knot-relay.pdf":
                      upload_id: 700001
                      path: "/knot-relay.pdf"
                      directory: "/"
                      name: "knot-relay.pdf"
                      mime: "application/pdf"
                      extension: "pdf"
                      icon: "pdf"
                      bytes: 990000
                      size: "967 KB"
                      lost: false
                      temp: false
                      date_uploaded: "08/10/2025 22:25"
                      date_last_accessed: "10/10/2025 16:52"
                      access_count: 13
                      platform: "s3cache"
                      meta: [ ]
                      uploader_id: 0
                      preview: "/ext/programme/activities/attachments/files/?action=preview&…"
                      preview_sizes:
                        dialog: "/ext/programme/activities/attachments/files/?action=preview&size=dialog&…"
                      props: [ ]
                  config:
                    endpoints:
                      put: "put"
                      get: "get"
                      preview: "preview"
                    single_file_size_limit_human: "5.0 MB"
                    directory_limit: 99
                    chunked_uploads: false
                meta:
                  size:
                    remaining: false
                    remaining_human: false
                    total: false
                    total_human: false
  /v3/programme/{eveningId}/bookings:
    get:
      summary: Get meeting with parent bookings
      tags:
        - Programme
      description: The meeting record (same fields as `getProgramme`, numeric types) plus `bookings` for parent helper slots.
      parameters:
        - name: eveningId
          in: path
          required: true
          schema:
            type: string
          description: Meeting ID.
      responses:
        "200":
          description: Meeting with bookings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  eveningid: 9500001
                  sectionid: 45002
                  title: "Camp Skills Night"
                  notesforparents: "Bring a warm coat."
                  notesforhelpingparents: null
                  parentsrequired: 0
                  games: ""
                  prenotes: ""
                  postnotes: ""
                  leaders: ""
                  meetingdate: "2024-11-01"
                  starttime: "18:00:00"
                  endtime: "19:30:00"
                  primary_leader_id: 0
                  config:
                    has_badges: true
                  googlecalendar: null
                  soft_deleted: 0
                  latitude: null
                  longitude: null
                  location: null
                  bookings: [ ]
                meta: [ ]
  /v3/todo/projects/{sectionId}:
    get:
      summary: List to-do projects
      tags:
        - Programme
      description: |
        To-do projects (groups of tasks) for the section. Only an empty list
        has been observed, so the item shape is unknown.
      parameters:
        - name: sectionId
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      responses:
        "200":
          description: Projects.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta: [ ]
  /v3/todo/tasks/meeting/{eveningId}:
    get:
      summary: Get to-do tasks for a meeting
      tags:
        - Programme
      description: Tasks attached to a meeting. `meta.users` maps user IDs to the leaders tasks can be assigned to.
      parameters:
        - name: eveningId
          in: path
          required: true
          schema:
            type: string
          description: Meeting ID.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Tasks.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta:
                  can_read: true
                  can_edit: true
                  users:
                    "100001":
                      name: "Alex Scout"
                      photo_url: null
                      user_id: 100001
  /ext/mymember/programme/leader_resources/:
    get:
      summary: Meeting leader-resources manifest
      tags:
        - Files
      description: |
        Read-only file manifest of leader resources shared to the Parent
        Portal for a meeting (`id` = meeting ID). Upload endpoints are `false`
        in `config.endpoints`. See [Files](#tag/Files).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Meeting ID.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: path
          in: query
          required: false
          schema:
            type: string
        - name: temp
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/FileManifest"
  /ext/mymember/events/leader_resources/:
    get:
      summary: Event leader-resources manifest
      tags:
        - Files
      description: Read-only file manifest of leader resources for an event. See [Files](#tag/Files).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Event ID.
        - name: event_id
          in: query
          required: true
          schema:
            type: string
          description: Event ID.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: path
          in: query
          required: false
          schema:
            type: string
        - name: temp
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/FileManifest"
  /ext/recipes/:
    get:
      summary: List recipes
      tags:
        - Programme
      description: |
        The recipe database (OSM-wide and the section's own). Each recipe has
        meal/cooking-method tags, a `suitability` map of dietary flags (`true`
        = suitable for that diet / free from that allergen), and an estimated
        `my_cost_per_person` in pounds.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getRecipes ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Recipes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - recipe_id: 5
                    title: "Campfire Bananas"
                    description: "Easy dessert cooked in foil."
                    cache:
                      tags:
                        meals: [ "pudding" ]
                        cooking_methods: [ "campfire" ]
                      suitability:
                        vegetarian: true
                        vegan: false
                        gluten: true
                        dairy: false
                        nuts: true
                        wheat: true
                        fish: true
                        shellfish: true
                        egg: true
                        soya: true
                        oatmeal: true
                    cooking_time: "20"
                    image_lastupdated: null
                    last_updated: "2025-10-10 14:07:03"
                    image: "upload"
                    original_recipe_id: "0"
                    tags: [ "pudding", "campfire" ]
                    image_url: "/ext/recipes/thumbnails/?recipe_id=5"
                    review_summary:
                      recipe:
                        rating: 0
                        number_reviews: 0
                    my_cost_per_person: 0.35
                meta: [ ]
  /v3/risk_assessments/reviews/{sectionId}:
    get:
      summary: List risk assessment review status
      tags:
        - Risk Assessments
      description: |
        Review schedule for the section's risk assessments. Each entry gives
        when it was last reviewed and by whom, `next_review_on`,
        `days_until_due` (negative when overdue) and `status` (observed
        `overdue`). `type` is the kind of thing assessed (observed `programme`)
        and `associated_id` its ID. `is_implicit` appears to mark entries OSM
        created automatically. `meta.review_months` is the review interval.
      parameters:
        - name: sectionId
          in: path
          required: true
          schema:
            type: string
          description: Section ID.
      responses:
        "200":
          description: Review entries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - reviewed_on: "2025-09-10"
                    reviewed_by_name: "Alex Leader"
                    next_review_on: "2026-09-10"
                    status: "overdue"
                    days_until_due: -61
                    is_implicit: false
                    type: "programme"
                    associated_id: 45001
                    title: "General meeting risk assessment"
                    last_used_on: "2026-11-03"
                meta:
                  review_months: 12
  /ext/risk_assessments/:
    get:
      summary: Get a risk assessment
      tags:
        - Risk Assessments
      description: |
        The hazards recorded against a meeting, activity, or event. `data` is
        an array of hazards (a Dojo item store: `meta.itemstore` gives the
        identifier `hazard_id` and label `hazard`). For `activity` and
        `generic_event` assessments, `meta` also carries `review` (last review,
        or `null`) and `review_months` (review interval, observed `12`).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ getRiskAssessment ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: type
          in: query
          required: true
          schema:
            type: string
          description: |
            What the assessment is attached to. Observed: `meeting`,
            `activity`, `generic_event`.
        - name: associated_id
          in: query
          required: true
          schema:
            type: string
          description: |
            ID of the meeting / activity / event. Observed `0` with
            `type=generic_event` (the section's generic event assessment).
      responses:
        "200":
          description: Hazards.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta:
                  itemstore:
                    identifier: hazard_id
                    label: hazard
                  review: null
                  review_months: 12
  /ext/risk_assessments/uploads/:
    get:
      summary: Risk assessment attachments manifest
      tags:
        - Files
      description: File manifest for documents attached to a risk assessment. See [Files](#tag/Files).
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ manifest ]
        - name: id
          in: query
          required: true
          schema:
            type: string
          description: Meeting (or event) ID.
        - name: type
          in: query
          required: true
          schema:
            type: string
          description: Observed `meeting`.
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: path
          in: query
          required: false
          schema:
            type: string
          description: Observed `/meeting/`.
        - name: temp
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/FileManifest"
  /ext/generic/locks/:
    post:
      summary: Obtain or clear an edit lock
      tags:
        - Generic
      description: |
        Advisory locks that stop two leaders editing the same thing at once
        (observed for email campaigns, `item=campaign_<campaignId>`).

        - `obtain` — returns a `lock_id`; `duration` is in seconds and the UI
          re-obtains periodically while editing.
        - `clear` — releases a lock by `lock_id`.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ obtain, clear ]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                item:
                  type: string
                  description: "`obtain` — what to lock, e.g. `campaign_70001`."
                duration:
                  type: string
                  description: "`obtain` — seconds (observed `60`)."
                read_allowed:
                  type: string
                  enum: [ "true", "false" ]
                  description: "`obtain` — whether others may read while locked."
                write_allowed:
                  type: string
                  enum: [ "true", "false" ]
                  description: "`obtain` — whether others may write while locked."
                type:
                  type: string
                  description: "`obtain` — observed `write`."
                lock_id:
                  type: string
                  description: "`clear` — lock to release."
                section_id:
                  type: string
            examples:
              obtain:
                summary: Obtain a lock
                value:
                  item: "campaign_70001"
                  duration: "60"
                  read_allowed: "false"
                  write_allowed: "false"
                  type: write
                  section_id: "45002"
              clear:
                summary: Clear a lock
                value:
                  lock_id: "9900001"
                  section_id: "45002"
      responses:
        "200":
          description: Lock result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              examples:
                obtain:
                  summary: Lock obtained
                  value:
                    status: true
                    error: null
                    data:
                      lock_id: 9900001
                    meta: [ ]
                clear:
                  summary: Lock cleared
                  value:
                    status: true
                    error: null
                    data: [ ]
                    meta: [ ]
  /ext/generic/featureusage/:
    post:
      summary: Record feature usage (telemetry)
      tags:
        - Generic
      description: |
        UI telemetry — the web app reports actions such as
        `viewed attendance register` or `drag drop patrols`. Third-party
        integrations have no need to call this.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ add ]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                feature:
                  type: string
                section_id:
                  type: string
            example:
              feature: "viewed attendance register"
              section_id: "45002"
      responses:
        "200":
          description: Recorded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
  /ext/users/auth/:
    post:
      summary: Session keep-alive ping
      tags:
        - User
      description: |
        Polled by the web app to keep the session alive and detect inactivity.
        `inactive` appears to be the number of seconds since the user's last
        interaction.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ ping ]
        - name: user_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Ping result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  inactive:
                    type: integer
              example:
                inactive: 34
  /ext/users/notifications/:
    get:
      summary: Get user notifications
      tags:
        - User
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ get ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Notifications.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  - level: 2
                    name: "SetRetentionSettings"
                    title: "Review your data retention settings"
                    hide_dismiss: false
                    dismiss: "SetRetentionSettings-45001-0a1b2c3d4e5f60718293a4b5c6d7e8f9"
                    dismiss_expires: "2026-11-17"
                    view_component: false
                    view_function: false
                    view_args: [ ]
                    view_constructor_args: { }
                    use_slider: false
                    video: false
                    image: false
                    data: [ "Set how long OSM keeps data about members who have left." ]
                    skip_if_dialogs_shown: false
                    actions:
                      - label: "Open settings"
                        component: "settings"
                        view_function: "open"
                        arguments: [ "retention" ]
                        dismiss: "2026-11-17"
                        colour: "blue"
                        icon: "cog"
                        size: "small"
                meta: [ ]
    post:
      summary: Dismiss a notification
      tags:
        - User
      description: |
        Hides a notification until `expires`. `item` is the notification's
        `dismiss` key from the GET response
        (`<NotificationName>-<sectionId>-<hash>`). The response returns the
        remaining `notifications`.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ dismiss ]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [ section_id, item ]
              properties:
                section_id:
                  type: string
                item:
                  type: string
                  description: The notification's `dismiss` key.
                expires:
                  type: string
                  format: date
                  description: Date until which the notification stays hidden (observed one week ahead).
            example:
              section_id: "45001"
              item: "SetRetentionSettings-45001-0a1b2c3d4e5f60718293a4b5c6d7e8f9"
              expires: "2026-11-17"
      responses:
        "200":
          description: Notification dismissed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  hidden: true
                  notifications: [ ]
                meta: [ ]
  /ext/settings/accountpreferences/:
    post:
      summary: Set the user's default section
      tags:
        - User
      description: Sets which section opens by default after login (the `isDefault` flag in startup `roles`). No request body — everything is in the query string.
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum: [ updateDefaultSection ]
        - name: section_id
          in: query
          required: true
          schema:
            type: string
        - name: section
          in: query
          required: true
          schema:
            type: string
          description: Section type.
      responses:
        "200":
          description: Default section updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data: [ ]
                meta: [ ]
  /v3/menu-preferences/{sectionid}:
    get:
      summary: Get menu preferences
      tags:
        - User
      description: The user's customised navigation menu for the section (`hidden` items and custom `order`).
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
      responses:
        "200":
          description: Menu preferences.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  hidden: [ ]
                  order: { }
                meta: [ ]
  /v3/favourites/{sectionid}:
    get:
      summary: Get favourites
      tags:
        - User
      parameters:
        - $ref: "#/components/parameters/SectionIdPath"
        - name: term_id
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: The user's favourited items.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V3Envelope"
              example:
                status: true
                error: null
                data:
                  items: [ ]
                meta: [ ]
components:
  securitySchemes:
    oauth2:
      type: oauth2
      description: |
        OSM uses OAuth 2.0 Authorization Code flow. Create an OAuth application
        at **Settings → My Account Details → Developer Tools** in the OSM UI to
        obtain your `client_id` and `client_secret`.
      flows:
        authorizationCode:
          authorizationUrl: https://www.onlinescoutmanager.co.uk/oauth/authorize
          tokenUrl: https://www.onlinescoutmanager.co.uk/oauth/token
          refreshUrl: https://www.onlinescoutmanager.co.uk/oauth/token
          scopes:
            section:member:read: Read member data for sections you have access to
            section:member:write: Update member data (patrols, ranks, fields)
            section:badge:read: Read badge records and progress
            section:event:read: Read events and programme data
            section:programme:read: Read programme summaries and meetings
  parameters:
    SectionIdPath:
      name: sectionid
      in: path
      required: true
      schema:
        type: string
      description: Section ID.
    EventIdPath:
      name: eventId
      in: path
      required: true
      schema:
        type: string
      description: Event ID.
  responses:
    FileManifest:
      description: File manifest (see the Files tag for the shape).
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/FileManifestResponse"
          example:
            status: true
            error: null
            data:
              manifest: [ ]
              config:
                endpoints:
                  put: "put"
                  get: "get"
                  preview: "preview"
                  rename: "rename"
                  move: "move"
                  delete: "delete"
                  createDirectory: "createDirectory"
                  getDirectory: "getDirectory"
                  renameDirectory: "renameDirectory"
                  moveDirectory: "moveDirectory"
                  deleteDirectory: "deleteDirectory"
                  putTemp: "putTemp"
                  commitTemp: "commitTemp"
                  revertTemp: "revertTemp"
                  partPut: "partPut"
                can_delete_own_files: false
                allowed_extensions: [ "pdf", "bmp", "gif", "jpg", "png" ]
                allowed_mime: [ "application/pdf", "image/*" ]
                max_file_size: 26214400
                max_file_size_human: "25.0 MB"
                allow_empty: false
                single_file_size_limit: 10485760
                single_file_size_limit_human: "10.0 MB"
                max_files: 10
                directory_limit: 1
                total_files_size_limit: 40789606
                total_files_size_limit_human: "38.9 MB"
                upload_available_space: 10
                upload_limit: 10
                chunked_uploads: false
                preview_sizes: [ "dialog" ]
            meta:
              size:
                remaining: 40789606
                remaining_human: "38.9 MB"
                total: 40789606
                total_human: "38.9 MB"
  schemas:
    StartupConfigPayload:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: object
          properties:
            site_type:
              type: string
              description: Site variant identifier (e.g., `scouts`).
            site_folder:
              type: string
              description: Server-side folder path for site-specific assets.
            siteConfig:
              type: object
              description: Global feature flags, upgrade pricing, section type definitions, and configuration.
              properties:
                expense_cards:
                  type: object
                  additionalProperties: true
                online_payment_gateways:
                  type: array
                  items:
                    type: string
                beta:
                  type: array
                  description: Beta features available for opt-in.
                  items:
                    type: object
                    properties:
                      key:
                        type: string
                      title:
                        type: string
                      description:
                        type: string
                      role:
                        type: string
                      release_ts:
                        type: integer
                      feedback:
                        type: object
                        additionalProperties: true
                    additionalProperties: true
                boltons:
                  type: object
                  description: Short-code to bolton name mapping.
                  additionalProperties:
                    type: string
                upgrade_config:
                  type: object
                  description: Subscription feature prices, keyed by feature name.
                  additionalProperties:
                    type: object
                    properties:
                      price:
                        type: number
                sms_prices:
                  type: object
                  description: SMS bundle prices keyed by credit quantity.
                  additionalProperties:
                    type: number
                recipes:
                  type: object
                  description: Enumerated values for the recipe planner.
                  additionalProperties:
                    type: array
                    items:
                      type: string
                section_types:
                  type: object
                  description: Human-readable section type names keyed by country code then section type slug.
                  additionalProperties:
                    type: object
                    additionalProperties:
                      type: string
                badge_types:
                  type: object
                  additionalProperties:
                    type: array
                    items:
                      type: string
                section_type_setup:
                  type: object
                  description: Per-section-type capability flags and badge definitions.
                  additionalProperties:
                    type: object
                    additionalProperties: true
                kill:
                  type: object
                  description: Feature kill-switch states (all `false` means all services active).
                  additionalProperties:
                    type: boolean
                buy_links:
                  type: object
                  additionalProperties:
                    type: string
                risk_assessments_config:
                  type: object
                  additionalProperties: true
                communicatable_contact_groups:
                  type: object
                  additionalProperties:
                    type: object
                    additionalProperties:
                      type: string
                email_template_colours:
                  type: array
                  items:
                    type: string
                campaign_template_designs:
                  type: array
                  items:
                    type: string
                remove_member_allowed_functions:
                  type: object
                  additionalProperties:
                    type: array
                    items:
                      type: string
              additionalProperties: true
            patrolLabels:
              type: object
              description: Patrol/group label names per section type.
              additionalProperties:
                type: object
                properties:
                  patrol:
                    type: string
                  patrols:
                    type: string
            patrolRoles:
              type: object
              description: |
                Patrol role definitions per section type. Youth sections use an array ordered by level;
                adult sections use an object keyed by level number.
              additionalProperties:
                oneOf:
                  - type: array
                    items:
                      type: object
                      additionalProperties: true
                  - type: object
                    additionalProperties:
                      type: object
                      additionalProperties: true
            extraSectionTypes:
              type: object
              description: Display names for non-standard section types.
              additionalProperties:
                type: string
            featurelevels:
              type: object
              description: Minimum subscription level required to access each feature.
              additionalProperties:
                type: integer
            changedlevels:
              type: array
              items:
                type: object
                additionalProperties: true
            availableRoles:
              type: object
              description: Global role slugs mapped to display names.
              additionalProperties:
                type: string
            globalAccessRoles:
              type: array
              description: Access control rules granting global roles specific access types.
              items:
                type: object
                properties:
                  user_role:
                    type: string
                  access_type:
                    type: string
                  id:
                    type: string
                additionalProperties: true
            terminology:
              type: object
              description: Hierarchy level names for the site (Group, District, County, etc.).
              additionalProperties:
                type: string
            forms:
              type: array
              description: Dynamic form definitions (accident forms, medication history, etc.).
              items:
                type: object
                additionalProperties: true
            default_terminology:
              type: object
              additionalProperties:
                type: string
            leaderPatrolIds:
              type: array
              description: Patrol IDs reserved for leaders (typically -2 and -3).
              items:
                type: integer
          additionalProperties: true
        meta:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    StartupDataPayload:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: object
          properties:
            cdn:
              type: string
              description: Base CDN URL for static assets.
            resources:
              type: array
              items:
                type: string
              description: Ordered list of JS/CSS resource paths to load on startup.
            config_hash:
              type: string
              description: Hash representing the current server-side configuration state.
            required_cdn_built_files:
              type: object
              additionalProperties:
                type: string
              description: Named CDN file URLs required for the app (e.g., `osm.js`, `react.js`).
            cache_bust:
              type: string
              description: Query string cache-busting token derived from built file hashes.
            globals:
              type: object
              properties:
                post_login_action:
                  description: Action to perform after login, or `false` if none.
                  oneOf:
                    - type: boolean
                    - type: string
                theme:
                  type: string
                  description: UI theme name (e.g., `blue`).
                userid:
                  type: integer
                  description: Authenticated user's numeric ID.
                roles:
                  type: array
                  description: Sections the user has access to, including permissions and config.
                  items:
                    type: object
                    properties:
                      groupname:
                        type: string
                      groupid:
                        type: string
                      group_country:
                        type: string
                      sectionid:
                        type: string
                      sectionname:
                        type: string
                      section:
                        type: string
                        description: Section type slug (e.g., `cubs`, `scouts`, `adults`, `waiting`).
                      isDefault:
                        type: string
                      permissions:
                        type: object
                        additionalProperties: true
                      sectionConfig:
                        type: object
                        additionalProperties: true
                    additionalProperties: true
                active_user_member:
                  description: Whether the user is also a member record, or `false`.
                  oneOf:
                    - type: boolean
                    - type: integer
                associated_member_id:
                  description: Associated member ID if applicable, or `false`.
                  oneOf:
                    - type: boolean
                    - type: integer
                email:
                  type: string
                firstname:
                  type: string
                lastname:
                  type: string
                fullname:
                  type: string
                preferences:
                  type: object
                  additionalProperties: true
                  description: User-specific UI and behavioural preferences.
                member_access:
                  type: object
                  additionalProperties:
                    type: object
                    additionalProperties: true
                  description: Sections where the user has parent-portal member access, keyed by section ID.
                member_access_unactivated:
                  description: Whether any member access is pending activation.
                  oneOf:
                    - type: boolean
                    - type: object
                      additionalProperties: true
                terms:
                  type: object
                  additionalProperties:
                    type: array
                    items:
                      type: object
                      properties:
                        termid:
                          type: string
                        sectionid:
                          type: string
                        name:
                          type: string
                        startdate:
                          type: string
                        enddate:
                          type: string
                        master_term:
                          nullable: true
                        past:
                          type: boolean
                      additionalProperties: true
                  description: Terms for each section, keyed by section ID.
                global_roles:
                  type: array
                  items:
                    type: object
                    additionalProperties: true
                hasSectionAccess:
                  type: boolean
                notepads:
                  type: array
                  items:
                    type: object
                    additionalProperties: true
                sockets:
                  type: string
                  description: Comma-separated `sectionid-token` pairs for WebSocket connections.
                user_socket:
                  type: string
                  description: Personal WebSocket token for the authenticated user.
                session_id:
                  type: string
                security_prompt:
                  type: boolean
                user_has_image:
                  type: string
                  description: Path to the user's profile photo.
                user_hash:
                  type: string
                  description: HMAC hash used for client-side identity verification.
                user_hash_legacy:
                  type: string
                language:
                  type: string
                  example: "en-GB"
                gamification:
                  type: object
                  additionalProperties:
                    type: object
                    properties:
                      total_steps:
                        type: integer
                      steps_completed:
                        type: integer
                      id:
                        oneOf:
                          - type: integer
                          - type: string
                    additionalProperties: true
                gamification_rank:
                  type: integer
                gamification_total:
                  type: integer
                features_used:
                  type: array
                  items:
                    type: string
                  description: Log of feature interactions recorded for this user account.
                unread_message_count:
                  type: integer
                client_time:
                  type: string
                  description: Client Unix timestamp in milliseconds at the time of the request.
              additionalProperties: true
            dojo:
              type: object
              properties:
                cacheBust:
                  type: string
                locale:
                  type: string
              additionalProperties: true
          additionalProperties: true
        meta:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    MembersList:
      type: object
      properties:
        identifier:
          type: string
        photos:
          type: boolean
        items:
          type: array
          items:
            type: object
            additionalProperties: true
      required: [ identifier, items ]
      additionalProperties: true
    MemberDetail:
      type: object
      properties:
        ok:
          type: boolean
        read_only:
          type: array
          items:
            type: string
        data:
          type: object
          additionalProperties: true
        meta:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    MemberTransfers:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        meta:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    AttendanceList:
      type: object
      properties:
        identifier:
          type: string
        label:
          type: string
        items:
          type: array
          items:
            type: object
            additionalProperties: true
        meetings:
          type: object
          additionalProperties:
            type: string
      additionalProperties: true
    AttendanceBadgeRequirements:
      type: array
      items:
        $ref: "#/components/schemas/AttendanceBadgeRequirement"
    AttendanceBadgeRequirement:
      type: object
      properties:
        badge:
          type: string
        badge_id:
          type: string
        badge_version:
          type: string
        column_id:
          type: string
        badgeName:
          type: string
        badgetype:
          type: string
        columngroup:
          type: string
        columnname:
          type: string
        description:
          type: string
        data:
          type: string
        eveningid:
          type: integer
        meetingdate:
          type: string
        link_from:
          type: string
        name:
          type: string
        section:
          type: string
        sameas_counter:
          type: integer
        sectionid:
          type: integer
      additionalProperties: true
    OkResponse:
      type: object
      properties:
        ok:
          type: boolean
      required: [ ok ]
      additionalProperties: true
    SelectedContactEmails:
      type: object
      properties:
        emails:
          type: object
          additionalProperties:
            type: object
            properties:
              emails:
                type: array
                items:
                  type: string
              firstname:
                type: string
              lastname:
                type: string
              photo_guid:
                type: string
              member_id:
                type: integer
            additionalProperties: true
        count:
          type: integer
        blocks:
          type: array
          items:
            type: object
            additionalProperties: true
        daily_limit_reached:
          type: boolean
      additionalProperties: true
    CustomDataResponse:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            $ref: "#/components/schemas/CustomDataGroup"
        meta:
          type: object
          additionalProperties: true
      additionalProperties: true
    CustomDataGroup:
      type: object
      properties:
        group_id:
          type: integer
        config:
          nullable: true
          type: object
          additionalProperties: true
        group_type:
          type: string
        identifier:
          type: string
        name:
          type: string
        description:
          type: string
        description_mymember:
          type: string
        is_considered_core:
          type: string
        allow_new_columns:
          type: string
        display:
          type: string
        columns:
          type: array
          items:
            $ref: "#/components/schemas/CustomDataColumn"
        custom_order:
          type: integer
      additionalProperties: true
    CustomDataColumn:
      type: object
      properties:
        column_id:
          type: integer
        type:
          type: string
        required:
          type: string
        display_in_advanced_view:
          type: string
        display_if_empty:
          type: string
        hide_from_group_display:
          type: string
        config:
          type: object
          additionalProperties: true
        varname:
          type: string
        label:
          type: string
        value:
          nullable: true
        is_core:
          type: string
        order:
          type: string
        force_read_only:
          type: string
        special_permissions:
          type: string
        permissions:
          type: array
          items:
            type: string
        orig_label:
          type: string
      additionalProperties: true
    CustomDataUpdateResponse:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: object
          additionalProperties: true
        meta:
          type: object
          additionalProperties: true
      additionalProperties: true
    CensusDetails:
      type: object
      properties:
        identifier:
          type: string
        items:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    FlexiRecords:
      type: object
      properties:
        identifier:
          type: string
        label:
          type: string
        items:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    DeletableMembers:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        meta:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    ProgrammeSummary:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    ProgrammeDetails:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/ProgrammeMeeting"
      additionalProperties: true
    ProgrammeMeeting:
      type: object
      description: One meeting, as returned by `getProgramme` (in `items`) and `editEveningParts`.
      properties:
        eveningid:
          type: string
          description: Meeting ID.
        sectionid:
          type: string
        title:
          type: string
        notesforparents:
          type: string
          description: Notes shown to parents in the parent portal.
        notesforhelpingparents:
          type: string
          nullable: true
          description: Notes for parents on the helper rota.
        parentsrequired:
          type: string
          description: Number of parent helpers needed, as a string.
        prenotes:
          type: string
          description: Leader notes for the start of the meeting.
        games:
          type: string
          description: Leader notes for games.
        postnotes:
          type: string
          description: Leader notes for the end of the meeting.
        leaders:
          type: string
          description: Free text, typically the leaders running the meeting.
        meetingdate:
          type: string
          format: date
        starttime:
          type: string
          example: "19:00:00"
        endtime:
          type: string
          example: "20:45:00"
        primary_leader_id:
          type: string
          description: ID of the leader in charge.
        config:
          type: object
          properties:
            has_badges:
              type: boolean
          additionalProperties: true
        soft_deleted:
          type: string
          description: '"1" if the meeting has been deleted.'
        help:
          type: array
          description: Parent helpers. Items include `scoutid` and `scout` (full name).
          items:
            type: object
            additionalProperties: true
        unavailableleaders:
          type: array
          items:
            type: object
            additionalProperties: true
        activities:
          type: array
          items:
            type: object
            additionalProperties: true
        badgelinks:
          type: array
          description: Empty array when there are no links (PHP empty map).
          items:
            type: object
            additionalProperties: true
        seasonal_suggestions:
          type: array
          description: Activity ideas from OSM's shared activity library.
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    ProgrammeParentRotaMembers:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        meta:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    BadgeTagCloud:
      type: object
      properties:
        tags:
          type: object
          additionalProperties:
            type: integer
        tag_count:
          type: integer
        badges:
          type: object
          additionalProperties:
            type: integer
      additionalProperties: true
    RiskAssessmentCategories:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        meta:
          type: object
          additionalProperties: true
      additionalProperties: true
    DashboardNextThings:
      type: object
      properties:
        conf:
          type: object
          additionalProperties:
            type: integer
        is_full_admin:
          type: boolean
        permissions:
          type: object
          additionalProperties: true
        patrols:
          type: array
          items:
            type: object
            additionalProperties: true
        birthdays:
          type: array
          items:
            type: object
            additionalProperties: true
        programme:
          type: array
          items:
            type: object
            additionalProperties: true
        events:
          type: array
          items:
            type: object
            additionalProperties: true
        outstandingpayments:
          type: array
          items:
            type: object
            additionalProperties: true
        surveys:
          oneOf:
            - type: boolean
            - type: object
              additionalProperties: true
        notepad:
          type: object
          additionalProperties: true
        subscription_expiring_soon:
          type: boolean
        programme_reviews:
          type: array
          items:
            type: object
            additionalProperties: true
        deletable_members:
          type: object
          additionalProperties: true
        due_badges:
          type: object
          additionalProperties: true
        expense_cards:
          type: object
          additionalProperties: true
        feature_request_voting:
          type: object
          additionalProperties: true
        pending_expense_cardholder_application:
          type: object
          additionalProperties: true
        thanks:
          type: object
          additionalProperties: true
        unanswered_question:
          type: object
          additionalProperties: true
        user_to_do_tasks:
          type: object
          additionalProperties: true
        whats_new:
          type: object
          additionalProperties: true
      additionalProperties: true
    AuditTrailResponse:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            properties:
              date:
                type: string
                description: Human-readable date (DD/MM/YYYY HH:MM).
              date_raw:
                type: string
                description: Raw timestamp (YYYY-MM-DD HH:MM:SS).
              summary:
                type: string
                description: HTML summary of the change with `<strong>` highlights.
              verbose:
                type: string
                description: Extended details (usually empty).
              type:
                type: string
                enum: [ members, details, patrols, patrol-leader ]
                description: Category of change.
              code:
                type: string
                enum: [ add, field, new, add-to, promoted ]
                description: Specific action within the category.
              hidden:
                type: boolean
        meta:
          type: object
          properties:
            hidden:
              type: boolean
            note_types:
              type: array
              items:
                type: string
              description: Available note type filters.
      additionalProperties: true
    CustomDataColumnUpdateResponse:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: object
          properties:
            column_id:
              type: integer
            type:
              type: string
              description: Column type (e.g. `select`, `text`).
            required:
              type: string
              enum: [ "yes", "no" ]
            varname:
              type: string
              description: Variable name (e.g. `gender`).
            label:
              type: string
              description: Human-readable label.
            value:
              type: string
              description: The updated value.
            is_core:
              type: string
              enum: [ "yes", "no" ]
            order:
              type: string
            config:
              type: object
              additionalProperties: true
              description: Column configuration including options for select fields.
          additionalProperties: true
        meta:
          type: object
          properties:
            last_updated:
              type: object
              properties:
                last_updated:
                  type: string
                last_updated_by_name:
                  type: string
      additionalProperties: true
    BadgeRecordsResponse:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        meta:
          type: array
          items:
            type: object
      additionalProperties: true
    BadgesByMemberResponse:
      type: object
      properties:
        status:
          type: boolean
        error:
          nullable: true
        data:
          type: array
          items:
            type: object
            properties:
              scoutid:
                type: integer
              firstname:
                type: string
              lastname:
                type: string
              patrol:
                type: string
              active:
                type: boolean
              badges:
                type: array
                items:
                  type: object
                  properties:
                    badge_id:
                      type: integer
                    badge_version:
                      type: integer
                    completed:
                      type: string
                    awarded:
                      type: string
                    status:
                      type: number
                  additionalProperties: true
            additionalProperties: true
        meta:
          type: array
          items:
            type: object
      additionalProperties: true
    V3Envelope:
      type: object
      description: |
        Standard wrapper used by `/v3/` endpoints and newer `/ext/` actions.
        `data` and `meta` are endpoint-specific; an empty `meta` is serialised
        as `[]` rather than `{}`.
      properties:
        status:
          type: boolean
        error:
          type: string
          nullable: true
        data:
          description: Endpoint-specific payload (object, array, or null).
        meta:
          description: Endpoint-specific metadata (object, or `[]` when empty).
      additionalProperties: true
    ItemStore:
      type: object
      description: |
        Dojo data store format used by older `/ext/` endpoints: `identifier`
        names the unique key in each item, `label` the display field.
      properties:
        identifier:
          type: string
        label:
          type: string
        items:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    GridStructure:
      type: array
      description: |
        Dojo grid layout: an array of column blocks, each with `rows` of column
        definitions (`name`, `field`, `width`, optional `formatter`, `tooltip`,
        `editable`). A block with `noscroll: true` is the frozen left block.
      items:
        type: object
        properties:
          rows:
            type: array
            items:
              type: object
              additionalProperties: true
          noscroll:
            type: boolean
        additionalProperties: true
    AttendanceBadgeRequirementsV2:
      type: object
      properties:
        badges:
          type: array
          items:
            $ref: "#/components/schemas/AttendanceBadgeRequirement"
        badges_updated_at:
          type: array
          items:
            type: object
            properties:
              user_name:
                type: string
              date_time:
                type: string
                description: "`dd/mm/yyyy hh:mm`."
      additionalProperties: true
    EmailConfig:
      type: object
      properties:
        found:
          type: boolean
        result:
          type: object
          properties:
            sectionid:
              type: string
            slug:
              type: string
              description: Newsletter slug for the section.
            numemails:
              type: string
              description: Number of emails sent (as string).
            live:
              type: string
              enum: [ "0", "1" ]
              description: Whether the newsletter is live.
        events:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              slug:
                type: string
          description: Upcoming events that can be used as email filters.
        patrols:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              slug:
                type: string
          description: Patrols/sixes in the section.
        leaders:
          type: boolean
          description: Whether leaders are available as recipients.
      additionalProperties: true
    FileManifestResponse:
      type: object
      description: |
        Shared response for every file-attachment `manifest` action. `manifest`
        is `[]` when empty, otherwise an object keyed by file path. `config`
        describes upload rules; `config.endpoints` maps each operation to the
        `action` value to call on the same URL (or `false` when not permitted).
      properties:
        status:
          type: boolean
        error:
          type: string
          nullable: true
        data:
          type: object
          properties:
            manifest:
              oneOf:
                - type: array
                  maxItems: 0
                  items: { }
                - type: object
                  additionalProperties:
                    type: object
                    properties:
                      upload_id:
                        type: integer
                      path:
                        type: string
                      directory:
                        type: string
                      name:
                        type: string
                      mime:
                        type: string
                      extension:
                        type: string
                      bytes:
                        type: integer
                      size:
                        type: string
                      date_uploaded:
                        type: string
                      preview:
                        type: string
                    additionalProperties: true
            config:
              type: object
              properties:
                endpoints:
                  type: object
                  additionalProperties: { }
                allowed_extensions:
                  type: array
                  items:
                    type: string
                allowed_mime:
                  type: array
                  items:
                    type: string
                max_file_size:
                  nullable: true
                  type: integer
                single_file_size_limit:
                  nullable: true
                  type: integer
                max_files:
                  description: Integer limit, `null`, or `false` when unlimited.
                chunked_uploads:
                  type: boolean
              additionalProperties: true
        meta:
          type: object
          additionalProperties: true
      additionalProperties: true
