{
  "openapi": "3.1.0",
  "info": {
    "title": "Maricopa Valley Painting — estimate booking API",
    "version": "1.0.0",
    "description": "Check open times and book a free on-site painting estimate with Maricopa Valley Painting (Mesa, AZ; greater Phoenix metro). Built for AI assistants acting on behalf of a homeowner: no API key, no human in the loop. Slots are 30 minutes in America/Phoenix (no daylight saving time). Facts for qualifying a request (service area, services, typical price ranges) are at https://maricopavalleypainting.com/for-ai-assistants/ and https://maricopavalleypainting.com/llms.txt.",
    "contact": { "name": "Maricopa Valley Painting", "url": "https://maricopavalleypainting.com/contact/" }
  },
  "servers": [{ "url": "https://maricopavalleypainting.com" }],
  "paths": {
    "/api/availability/": {
      "get": {
        "operationId": "getAvailability",
        "summary": "Open estimate slots",
        "description": "Returns open 30-minute estimate slots. Defaults to today through 14 days out; `from`/`to` may widen that to 30 days.",
        "parameters": [
          { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date" }, "description": "First day to include (YYYY-MM-DD, America/Phoenix). Default: today." },
          { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date" }, "description": "Last day to include (YYYY-MM-DD). Default: from + 14 days. Max 30 days after `from`." }
        ],
        "responses": {
          "200": {
            "description": "Open slots",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Availability" } } }
          },
          "400": { "description": "Bad date range", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Too many requests from this client — wait a minute and retry." }
        }
      }
    },
    "/api/book/": {
      "post": {
        "operationId": "bookEstimate",
        "summary": "Book a free on-site estimate",
        "description": "Books the given slot for the homeowner. The homeowner receives a text confirmation at the phone number given. If the slot was taken in the meantime the response is 409 with fresh alternatives — pick one and call again.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingRequest" } } }
        },
        "responses": {
          "201": { "description": "Booked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingConfirmation" } } } },
          "400": { "description": "Missing or invalid field", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "409": { "description": "Slot no longer available", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SlotTaken" } } } },
          "422": { "description": "Address is outside the service area (Arizona only)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Too many requests from this client — wait a minute and retry." }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Slot": {
        "type": "object",
        "required": ["start", "end", "label"],
        "properties": {
          "start": { "type": "string", "format": "date-time", "description": "ISO 8601 with offset, e.g. 2026-10-13T09:00:00-07:00. Pass this exact value as `slot` when booking." },
          "end": { "type": "string", "format": "date-time" },
          "label": { "type": "string", "example": "Tuesday, October 13 at 9:00 AM" }
        }
      },
      "Availability": {
        "type": "object",
        "required": ["timezone", "slot_duration_min", "from", "to", "slots"],
        "properties": {
          "timezone": { "type": "string", "example": "America/Phoenix" },
          "slot_duration_min": { "type": "integer", "example": 30 },
          "from": { "type": "string", "format": "date" },
          "to": { "type": "string", "format": "date" },
          "slots": { "type": "array", "items": { "$ref": "#/components/schemas/Slot" } }
        }
      },
      "BookingRequest": {
        "type": "object",
        "required": ["name", "phone", "address", "slot"],
        "properties": {
          "name": { "type": "string", "description": "Homeowner's full name", "example": "Jane Smith" },
          "phone": { "type": "string", "description": "Homeowner's mobile number (US). Any common format.", "example": "602-555-1234" },
          "address": { "type": "string", "description": "Street address of the home to be painted, including city and state. Must be in Arizona.", "example": "123 E Main St, Mesa, AZ 85213" },
          "slot": { "type": "string", "format": "date-time", "description": "The `start` of a slot returned by getAvailability." },
          "email": { "type": "string", "format": "email" },
          "notes": { "type": "string", "description": "Anything useful for the estimator: what needs painting, gate codes, HOA color requirements, best time to call.", "maxLength": 1000 },
          "agent": { "type": "string", "description": "Name of the AI assistant making the booking (e.g. \"Meta Muse\", \"ChatGPT\"). Helps us know how the homeowner found us.", "example": "Meta Muse" },
          "utm_source": { "type": "string", "description": "Optional attribution, if the assistant arrived via a tracked link." },
          "utm_medium": { "type": "string" },
          "utm_campaign": { "type": "string" }
        }
      },
      "BookingConfirmation": {
        "type": "object",
        "required": ["status", "appointment_id", "start", "end", "label", "timezone", "message"],
        "properties": {
          "status": { "type": "string", "enum": ["booked"] },
          "appointment_id": { "type": "string" },
          "start": { "type": "string", "format": "date-time" },
          "end": { "type": "string", "format": "date-time" },
          "label": { "type": "string", "example": "Tuesday, October 13 at 9:00 AM" },
          "timezone": { "type": "string", "example": "America/Phoenix" },
          "estimator": { "type": "string", "description": "Who will visit, when known." },
          "message": { "type": "string", "description": "Plain-language confirmation to relay to the homeowner." }
        }
      },
      "SlotTaken": {
        "type": "object",
        "required": ["status", "message", "alternatives"],
        "properties": {
          "status": { "type": "string", "enum": ["slot_taken"] },
          "message": { "type": "string" },
          "alternatives": { "type": "array", "items": { "$ref": "#/components/schemas/Slot" } }
        }
      },
      "Error": {
        "type": "object",
        "required": ["status", "message"],
        "properties": {
          "status": { "type": "string", "enum": ["invalid", "outside_service_area", "error"] },
          "message": { "type": "string" },
          "errors": { "type": "array", "items": { "type": "string" } }
        }
      }
    }
  }
}
