openapi: 3.0.0
info:
  title: BudgetFitter AI Toolset
  description: API for AI agents to retrieve verified discount data and brand intelligence.
  version: 1.1.0
servers:
  - url: https://budgetfitter.uk
paths:
  /brand/{slug}/:
    get:
      operationId: getBrandData
      summary: Get brand data
      description: Returns AI-optimized data for a specific brand. Use ?view=ai for Markdown or ?view=json for JSON.
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
        - name: view
          in: query
          required: false
          schema:
            type: string
            enum: [ai, json]
            default: ai
      responses:
        "200":
          description: Brand data in Markdown (view=ai) or JSON (view=json) format
          content:
            text/markdown:
              schema:
                type: string
            application/json:
              schema:
                type: object
                properties:
                  title:
                    type: string
                  slug:
                    type: string
                  url:
                    type: string
                  deals:
                    type: array
                    items:
                      type: object
        "404":
          description: Brand not found
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"
        "429":
          description: Rate limit exceeded
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetail"
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
  /llms-full.txt:
    get:
      operationId: getLlmsFullIndex
      summary: Full index
      description: Comprehensive list of all brands and categories.
      responses:
        "200":
          description: Full brand/category index in plain text
          content:
            text/plain:
              schema:
                type: string
  /llms.txt:
    get:
      operationId: getLlmsSummary
      summary: LLMs summary
      description: Concise AI agent summary of the site.
      responses:
        "200":
          description: Site summary for LLMs in plain text
          content:
            text/plain:
              schema:
                type: string
  /mcp:
    post:
      operationId: postMcpJsonRpc
      summary: MCP Server (JSON-RPC 2.0)
      description: |
        Model Context Protocol endpoint using Streamable HTTP transport.
        Supports methods: initialize, tools/list, tools/call, ping.
        Available tools: search, brand_lookup, get_llms_index, navigate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jsonrpc, method]
              properties:
                jsonrpc:
                  type: string
                  enum: ["2.0"]
                id:
                  oneOf:
                    - type: string
                    - type: integer
                method:
                  type: string
                  enum: [initialize, initialized, ping, tools/list, tools/call]
                params:
                  type: object
            examples:
              initialize:
                summary: Initialize session
                value:
                  jsonrpc: "2.0"
                  id: 1
                  method: initialize
                  params:
                    protocolVersion: "2025-03-26"
                    clientInfo:
                      name: example-client
                      version: "1.0"
                    capabilities: {}
              tools_list:
                summary: List available tools
                value:
                  jsonrpc: "2.0"
                  id: 2
                  method: tools/list
              tools_call_search:
                summary: Call the search tool
                value:
                  jsonrpc: "2.0"
                  id: 3
                  method: tools/call
                  params:
                    name: search
                    arguments:
                      query: nike discount code
                      limit: 5
      responses:
        "200":
          description: JSON-RPC 2.0 response
          content:
            application/json:
              schema:
                type: object
                properties:
                  jsonrpc:
                    type: string
                  id:
                    oneOf:
                      - type: string
                      - type: integer
                  result:
                    type: object
                  error:
                    type: object
                    properties:
                      code:
                        type: integer
                      message:
                        type: string
        "405":
          $ref: "#/components/responses/MethodNotAllowed"
        "429":
          $ref: "#/components/responses/RateLimited"
  /ask:
    get:
      operationId: getNlwebAsk
      summary: NLWeb /ask (GET)
      description: |
        Search BudgetFitter using natural language via query params.
        Returns structured results matching the Microsoft NLWeb protocol.
      parameters:
        - name: query
          in: query
          required: true
          schema:
            type: string
          description: Natural language search query (alias: q)
        - name: q
          in: query
          schema:
            type: string
          description: Alias for query parameter
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 5
        - name: mode
          in: query
          schema:
            type: string
            enum: [list, summary]
            default: list
      responses:
        "200":
          description: NLWeb search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NLWebResponse"
            text/event-stream:
              schema:
                type: string
                description: SSE stream with start, result, and complete events
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/X-RateLimit-Limit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/RateLimited"
    post:
      operationId: postNlwebAsk
      summary: NLWeb /ask (POST)
      description: |
        Search BudgetFitter using natural language. Returns structured results matching
        the Microsoft NLWeb protocol. Supports JSON (default) and SSE streaming
        (set Accept: text/event-stream).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query:
                  type: string
                  description: Natural language search query (alias: q)
                q:
                  type: string
                  description: Alias for query
                limit:
                  type: integer
                  description: Maximum results (1-20)
                  default: 5
                mode:
                  type: string
                  enum: [list, summary]
                  default: list
                streaming:
                  type: boolean
                  description: Enable SSE streaming (default true if Accept includes text/event-stream)
            examples:
              search:
                summary: Search for deals
                value:
                  query: best Nike discount codes
                  limit: 5
      responses:
        "200":
          description: NLWeb search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NLWebResponse"
            text/event-stream:
              schema:
                type: string
                description: SSE stream with start, result, and complete events
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/X-RateLimit-Limit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
        "400":
          $ref: "#/components/responses/BadRequest"
        "405":
          $ref: "#/components/responses/MethodNotAllowed"
        "429":
          $ref: "#/components/responses/RateLimited"

  /webhooks:
    get:
      operationId: getWebhookDocs
      summary: Webhook documentation
      description: Returns webhook event types, subscription info, and security details.
      responses:
        "200":
          description: Webhook documentation JSON
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  events:
                    type: array
                    items:
                      type: object
                  subscribe:
                    type: object
                  security:
                    type: string

components:
  headers:
    X-RateLimit-Limit:
      description: Maximum requests per window
      schema:
        type: integer
        example: 100
    X-RateLimit-Remaining:
      description: Remaining requests in current window
      schema:
        type: integer
        example: 99
    X-RateLimit-Window:
      description: Rate limit window in seconds
      schema:
        type: integer
        example: 60
    Retry-After:
      description: Seconds to wait before retrying
      schema:
        type: integer
        example: 1

  responses:
    BadRequest:
      description: Bad request — missing or invalid parameters
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetail"
          example:
            type: "https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.1"
            title: Bad Request
            status: 400
            detail: "Missing required field: query"
    NotFound:
      description: Resource not found
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetail"
          example:
            type: "https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.5"
            title: Not Found
            status: 404
            detail: The requested resource was not found.
            help:
              docs: "https://budgetfitter.uk/llms.txt"
              openapi: "https://budgetfitter.uk/openapi.yaml"
              mcp: "https://budgetfitter.uk/.well-known/mcp"
    MethodNotAllowed:
      description: HTTP method not allowed
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetail"
    RateLimited:
      description: Rate limit exceeded — retry after the period indicated by Retry-After header
      headers:
        Retry-After:
          $ref: "#/components/headers/Retry-After"
        X-RateLimit-Limit:
          $ref: "#/components/headers/X-RateLimit-Limit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/X-RateLimit-Remaining"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetail"
          example:
            type: "https://datatracker.ietf.org/doc/html/rfc6585#section-4"
            title: Too Many Requests
            status: 429
            detail: "Rate limit exceeded. Please retry after the Retry-After period."

  schemas:
    ProblemDetail:
      type: object
      description: RFC 7807 Problem Details for HTTP APIs
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri
          description: A URI reference identifying the problem type
        title:
          type: string
          description: A short, human-readable summary of the problem type
        status:
          type: integer
          description: The HTTP status code
        detail:
          type: string
          description: A human-readable explanation specific to this occurrence
        instance:
          type: string
          description: A URI reference identifying this specific occurrence
        help:
          type: object
          description: Helpful links for error recovery
          properties:
            docs:
              type: string
              format: uri
            openapi:
              type: string
              format: uri
            mcp:
              type: string
              format: uri

    NLWebResponse:
      type: object
      description: Microsoft NLWeb protocol response
      properties:
        status:
          type: string
          example: success
        query:
          type: string
        query_id:
          type: string
          format: uuid
        mode:
          type: string
          enum: [list, summary]
        count:
          type: integer
        site:
          type: string
        "@context":
          type: string
          example: "https://schema.org"
        results:
          type: array
          items:
            type: object
            properties:
              "@type":
                type: string
              url:
                type: string
                format: uri
              name:
                type: string
              site:
                type: string
              score:
                type: number
                minimum: 0
                maximum: 1
              description:
                type: string
              schema_object:
                type: object

  # Webhook callbacks
  callbacks:
    brandUpdated:
      "{$request.body#/url}":
        post:
          summary: Brand updated webhook
          description: Fired when a brand page is updated with new deals or codes.
          requestBody:
            content:
              application/json:
                schema:
                  type: object
                  properties:
                    event:
                      type: string
                      example: brand.updated
                    brand_slug:
                      type: string
                    brand_name:
                      type: string
                    deal_count:
                      type: integer
                    updated_at:
                      type: string
                      format: date-time
          responses:
            "200":
              description: Webhook received
