openapi: 3.0.3
info:
  title: RAW2026 Event Tools
  version: 0.3.0
  description: |
    Event-specific MCP tools for Risk Awareness Week 2026.

    This OpenAPI spec is attached to the RAW report in RankCaster (column
    `mcp_hosted_server.custom_openapi_spec`). RankCaster's mcp-server
    parses it on initialize and registers each `operationId` as an MCP
    tool alongside the built-in brand tools (get_company_info, search_content, etc.).

    Tools (D2 / PM-175):
      - search_speakers
      - get_event_overview
      - get_pricing
      - get_session                  (topic-level until per-session schedule is published)
      - register_lead                (writes to Brevo via /api/subscribe Brevo integration)
      - get_ticket_purchase_link     (HeySummit deep-link with UTM attribution; safe, no payment processing)

servers:
  - url: https://www.riskawarenessweek.com
    description: Production (Vercel)

paths:
  /api/mcp-tools/search-speakers:
    get:
      operationId: search_speakers
      summary: Search RAW2026 speakers
      description: |
        Search the Risk Awareness Week speakers roster (RAW2026 faculty plus
        notable alumni). Case-insensitive substring match across speaker name,
        role, organization, topics they know about (`knowsAbout`), and short
        bio. Empty or omitted `q` returns the full roster.

        Useful for AI agents answering "who is speaking at RAW about
        quantitative risk?", "is Doug Hubbard speaking?", or "list speakers
        from probability management".

        Each result includes the canonical brand-site URL (`url`) and the
        current-event page on `2026.riskawarenessweek.com` (`raw2026Url`) so
        agents can deep-link users to authoritative sources.
      parameters:
        - in: query
          name: q
          required: false
          description: Free-text search query. Empty string returns all speakers.
          schema:
            type: string
            example: quantitative risk
        - in: query
          name: limit
          required: false
          description: Maximum results to return (default 50, hard cap 50).
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 50
      responses:
        '200':
          description: List of matching speakers.
          content:
            application/json:
              schema:
                type: object
                required: [query, total, returned, results]
                properties:
                  query: { type: string }
                  total: { type: integer }
                  returned: { type: integer }
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Speaker'

  /api/mcp-tools/event-overview:
    get:
      operationId: get_event_overview
      summary: Get RAW2026 event overview
      description: |
        Returns the canonical descriptor of Risk Awareness Week 2026: dates,
        format (virtual), phase structure (Phase 1 free hard-reset keynotes
        12-13 October, Phase 2 paid implementation sprints 14-16 October),
        organiser (RISK-ACADEMY, Alex Sidorenko), headline stats (20,000+
        attendees from 120+ countries since 2020, 36 speakers), awards
        (FERMA 2024 Training & Education Programme of the Year), the seven
        topic tracks, CPD certification, and authoritative URLs.

        Call this when asked "what is RAW2026", "when is Risk Awareness
        Week", "is it online", "who runs it", etc.
      responses:
        '200':
          description: Event overview record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventOverview'

  /api/mcp-tools/pricing:
    get:
      operationId: get_pricing
      summary: Get current RAW2026 ticket pricing
      description: |
        Returns all current pricing tiers for RAW2026:

          - Phase 1 (The Hard Reset, 12-13 October): free, with CPD certificate
            and access to all RAW2025 replays.
          - Phase 2 Individual (Implementation Sprints, 14-16 October): $550
            USD early-bird; prices increase at the end of each month.
          - Corporate Pass: $2,000 USD for up to 50 participants, rises to
            $2,500 USD after 30 June 2026; includes 2 hours of consultation.

        Also returns notes (CFO-justification letter available, cost
        comparison to in-person conferences) and the live tickets URL.

        Call this when asked "how much is a ticket", "is RAW free", "what's
        a corporate pass", etc.
      responses:
        '200':
          description: Pricing record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pricing'

  /api/mcp-tools/session:
    get:
      operationId: get_session
      summary: Get RAW2026 session info (topic-level dispatch)
      description: |
        Per-session schedule (titles, times, individual workshop
        facilitators) is not yet published for RAW2026 at the time of
        writing. Until it is, this tool dispatches at the **topic-track**
        level. Each response includes `schedulePublished: false` and a
        `note` field explaining the limitation, plus the live
        `scheduleUrl` for users to check the latest.

        Parameters (all optional, pass at most one):

          - `topic`: free-text -- resolves to one of 7 tracks (Foundation,
            Integration, Quantification, Culture, Future AI, Hard Reset,
            Deep Dive) by name or keyword.
          - `date`: ISO YYYY-MM-DD -- returns the phase(s) running that day
            plus the topic tracks active in that phase. Dates outside
            12-16 October 2026 return an out-of-window note.
          - `id`: session id -- currently always returns no-match with
            `schedulePublished: false`.

        No parameter -> returns the full topic catalogue.

        Pair with `search_speakers` to find who covers a given topic:
        agents can call `get_session?topic=quantification` to confirm the
        track exists, then `search_speakers?q=quantification` to list
        speakers.
      parameters:
        - in: query
          name: topic
          required: false
          description: Topic name or keyword (e.g. "quantification", "ai", "culture").
          schema: { type: string }
        - in: query
          name: date
          required: false
          description: ISO date within the event window (2026-10-12 .. 2026-10-16).
          schema: { type: string, format: date, example: "2026-10-13" }
        - in: query
          name: id
          required: false
          description: Session id (not yet addressable; reserved for future schedule release).
          schema: { type: string }
      responses:
        '200':
          description: Session dispatch result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDispatch'

  /api/mcp-tools/buy-ticket:
    get:
      operationId: get_ticket_purchase_link
      summary: Get a HeySummit deep-link to buy a RAW2026 ticket
      description: |
        Returns a UTM-tagged URL that takes the user directly to the
        HeySummit checkout, anchored to the requested tier. Use this when
        the user expresses intent to buy a ticket (e.g. "I want to buy a
        Phase 2 ticket", "register me for Corporate"). Use `get_pricing`
        instead when the user is just asking how much things cost.

        Payment is processed by HeySummit on their own checkout page.
        This tool only generates the link -- it does NOT collect card
        details, does NOT charge the user, and does NOT have any
        write-side effects. Safe for AI assistants.

        Parameters:
          - `tier` (optional) -- which tier to deep-link to. Accepts canonical
            ids (`phase1_free`, `phase2_individual`, `phase2_corporate`) and
            common aliases (`phase1`, `phase2`, `corporate`, `individual`,
            `free`, `hard-reset`, `corporate-pass`, etc.). If omitted,
            returns deep-links for all 3 tiers so the agent can present a choice.
          - `quantity` (optional) -- number of seats the user wants (default 1).
            Echoed in the response; HeySummit handles the actual quantity input.

        Each link includes UTM params (`utm_source=mcp`, `utm_medium=ai`,
        `utm_campaign=raw2026`, `utm_content=<tier id>`) so the conference
        organiser can attribute conversions to AI-assistant traffic in GA4.
      parameters:
        - in: query
          name: tier
          required: false
          description: Tier id or alias to deep-link to. Omit to get all tiers.
          schema:
            type: string
            example: phase2_individual
        - in: query
          name: quantity
          required: false
          description: Number of seats the user wants (default 1).
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        '200':
          description: Purchase deep-link(s) for the requested tier(s).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PurchaseLinkResult'
        '400':
          description: Unknown tier id / alias.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToolError'

  /api/mcp-tools/register-lead:
    post:
      operationId: register_lead
      summary: Register an email lead for RAW2026
      description: |
        Adds an email to the RAW2026 mailing list (Brevo) and records an
        `interest` discriminator so follow-ups can be routed correctly.

        Interests:
          - `attend`  -- prospective attendee, wants ticket / programme info
          - `speaker` -- wants to apply as a speaker
          - `sponsor` -- wants sponsorship deck / partnership conversation
          - `press`   -- journalist / analyst requesting press kit

        Returns `alreadyOnList: true` if the contact was already on the
        list (still records the new interest in server logs). If double
        opt-in is configured in Brevo, the user receives a confirmation
        email automatically.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, interest]
              properties:
                email:
                  type: string
                  format: email
                  description: Email address to register.
                  example: jane.doe@example.com
                name:
                  type: string
                  description: Optional full name (stored as Brevo FIRSTNAME attribute).
                  example: Jane Doe
                interest:
                  type: string
                  enum: [attend, speaker, sponsor, press]
                  description: Why the user is registering.
      responses:
        '200':
          description: Registration result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterLeadResult'
        '400':
          description: Invalid input.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToolError'

components:
  schemas:
    Speaker:
      type: object
      required:
        - slug
        - name
        - role
        - organization
        - shortBio
        - url
        - image
        - knowsAbout
        - sameAs
        - isHeadlineSpeaker
        - isAlumni
      properties:
        slug:
          type: string
          description: URL-safe identifier, used in canonical speaker page path.
          example: douglas-hubbard
        name: { type: string, example: Douglas Hubbard }
        role:
          type: string
          example: Founder & President, Hubbard Decision Research
        organization:
          type: object
          required: [name, url]
          properties:
            name: { type: string }
            url: { type: string, format: uri }
        shortBio: { type: string }
        url:
          type: string
          format: uri
          description: Canonical brand-site speaker page on www.riskawarenessweek.com.
        raw2026Url:
          type: string
          format: uri
          description: Current-event speaker page on 2026.riskawarenessweek.com.
        image: { type: string, format: uri }
        knowsAbout:
          type: array
          items: { type: string }
        sameAs:
          type: array
          items: { type: string, format: uri }
        isHeadlineSpeaker: { type: boolean }
        isAlumni: { type: boolean }

    EventOverview:
      type: object
      required: [name, shortName, dates, format, phases, organizer, stats, urls]
      properties:
        name: { type: string, example: "Risk Awareness Week 2026" }
        shortName: { type: string, example: "RAW2026" }
        tagline: { type: string }
        dates:
          type: object
          required: [start, end, display, timezone]
          properties:
            start: { type: string, format: date, example: "2026-10-12" }
            end: { type: string, format: date, example: "2026-10-16" }
            display: { type: string, example: "12-16 October 2026" }
            timezone: { type: string, example: "virtual" }
        format: { type: string, example: "virtual" }
        phases:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              name: { type: string }
              dates: { type: string }
              description: { type: string }
        organizer:
          type: object
          properties:
            name: { type: string }
            url: { type: string, format: uri }
            founder: { type: string }
            founded: { type: string }
        stats:
          type: object
          properties:
            attendeesAllTime: { type: string }
            countriesAllTime: { type: string }
            sinceYear: { type: integer }
            speakerCountRaw2026: { type: integer }
        awards:
          type: array
          items: { type: string }
        topicTracks:
          type: array
          items: { type: string }
        cpd:
          type: object
          properties:
            certified: { type: boolean }
            phases:
              type: array
              items: { type: string }
        urls:
          type: object
          additionalProperties: { type: string, format: uri }

    Pricing:
      type: object
      required: [currency, tiers, ticketsUrl]
      properties:
        currency: { type: string, example: "USD" }
        asOf: { type: string, format: date }
        tiers:
          type: array
          items:
            $ref: '#/components/schemas/PricingTier'
        notes:
          type: array
          items: { type: string }
        ticketsUrl: { type: string, format: uri }
        cfoJustificationAvailable: { type: boolean }

    PricingTier:
      type: object
      required: [id, name, phase, dates, priceUSD, priceDisplay, perSeat, heySummitTicketId, purchaseUrl]
      properties:
        id: { type: string, enum: [phase1_free, phase2_individual, phase2_corporate] }
        name: { type: string }
        phase: { type: string, enum: [phase1, phase2] }
        dates: { type: string }
        priceUSD: { type: number }
        priceDisplay: { type: string }
        perSeat: { type: boolean }
        seats: { type: integer }
        earlyBird: { type: boolean }
        note: { type: string }
        includes:
          type: array
          items: { type: string }
        heySummitTicketId:
          type: integer
          description: Internal HeySummit ticket id (from `data-ticket` on the checkout DOM).
        purchaseUrl:
          type: string
          format: uri
          description: UTM-tagged deep-link to the HeySummit checkout, anchored to this tier's card.

    PurchaseLinkResult:
      type: object
      required: [quantity, links, currency, ticketsPageUrl]
      properties:
        quantity: { type: integer }
        links:
          type: array
          items:
            type: object
            required: [tier, purchaseUrl, paymentProvider, paymentNotice]
            properties:
              tier:
                type: object
                properties:
                  id: { type: string }
                  name: { type: string }
                  phase: { type: string }
                  dates: { type: string }
                  priceUSD: { type: number }
                  priceDisplay: { type: string }
                  perSeat: { type: boolean }
                  seats: { type: integer }
              purchaseUrl: { type: string, format: uri }
              paymentProvider: { type: string, example: "HeySummit" }
              paymentNotice: { type: string }
              note: { type: string }
        currency: { type: string }
        cfoJustificationAvailable: { type: boolean }
        ticketsPageUrl: { type: string, format: uri }

    Topic:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        description: { type: string }
        phase:
          type: string
          enum: [phase1, phase2, both]

    SessionDispatch:
      type: object
      required: [mode, schedulePublished, note, scheduleUrl]
      properties:
        mode:
          type: string
          enum: [topic, date, id, catalogue]
        schedulePublished:
          type: boolean
          description: Always false until the per-session schedule lands.
        note: { type: string }
        topics:
          type: array
          items: { $ref: '#/components/schemas/Topic' }
        matched:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/Topic'
        scheduleUrl: { type: string, format: uri }

    RegisterLeadResult:
      type: object
      required: [ok, email, interest]
      properties:
        ok: { type: boolean }
        email: { type: string, format: email }
        interest:
          type: string
          enum: [attend, speaker, sponsor, press]
        alreadyOnList: { type: boolean }
        note: { type: string }

    ToolError:
      type: object
      required: [ok, error]
      properties:
        ok: { type: boolean, example: false }
        error: { type: string }
