openapi: 3.1.0
info:
  title: Portugal Alojamento Tourist Tax API
  version: 1.0.0
  description: Versioned, source-backed municipal tourist-tax data for Portugal.
  license:
    name: CC BY 4.0
    url: https://creativecommons.org/licenses/by/4.0/
servers:
  - url: https://portugalalojamento.com/api/v1
paths:
  /municipalities:
    get:
      summary: List reviewed municipalities
      responses:
        "200":
          description: Municipality collection
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Collection" }
  /municipalities/{slug}:
    get:
      summary: Get one municipality and its current rule
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        "200": { description: Municipality record }
        "404": { $ref: "#/components/responses/NotFound" }
  /municipalities/{slug}/history:
    get:
      summary: Get published versions and recorded changes
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        "200": { description: Version history }
        "404": { $ref: "#/components/responses/NotFound" }
  /rules/current:
    get:
      summary: List current published rules
      responses:
        "200": { description: Current rule collection }
  /updates:
    get:
      summary: List recent verified dataset changes
      responses:
        "200": { description: Update collection }
  /calculate:
    post:
      summary: Calculate tax night by night
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CalculationInput" }
      responses:
        "200":
          description: Auditable calculation
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CalculationResponse" }
        "404": { $ref: "#/components/responses/NotFound" }
        "422": { description: Invalid input or calculation }
        "429": { description: Rate limit exceeded }
  /data.json:
    get:
      summary: Download the complete JSON dataset
      responses:
        "200": { description: Complete dataset }
  /data.csv:
    get:
      summary: Download the current-rule CSV dataset
      responses:
        "200":
          description: CSV download
          content:
            text/csv: {}
components:
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Collection:
      type: object
      required: [apiVersion, datasetGeneratedAt, count, data]
      properties:
        apiVersion: { type: string }
        datasetGeneratedAt: { type: string, format: date-time }
        count: { type: integer }
        data: { type: array, items: { type: object } }
    Guest:
      type: object
      required: [age]
      properties:
        age: { type: [integer, "null"], minimum: 0, maximum: 130 }
        exemption: { type: string }
    CalculationInput:
      type: object
      required: [municipality, checkIn, checkOut, guests]
      properties:
        municipality: { type: string, examples: [lisboa] }
        checkIn: { type: string, format: date }
        checkOut: { type: string, format: date }
        guests: { type: array, minItems: 1, maxItems: 50, items: { $ref: "#/components/schemas/Guest" } }
    CalculationResponse:
      type: object
      properties:
        apiVersion: { type: string }
        data:
          type: object
          required: [totalCents, taxableNights, taxableGuests, appliedRuleVersion, sources, breakdown, warnings]
          properties:
            totalCents: { type: integer }
            taxableNights: { type: integer }
            taxableGuests: { type: integer }
            appliedRuleVersion: { type: string }
            sources: { type: array, items: { type: object } }
            breakdown: { type: array, items: { type: object } }
            warnings: { type: array, items: { type: string } }
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string }
            message: { type: string }
            details: {}
