{
  "openapi": "3.1.0",
  "info": {
    "title": "A Superior Mechanical Private Mobile API",
    "version": "1.0.1",
    "description": "Private API used by the A Superior Mechanical native iPhone/iPad app for service discovery, lead capture, booking requests, and ServiceTitan proxy operations. The app should call this API, not ServiceTitan directly."
  },
  "servers": [
    {
      "url": "https://asuperiormechanical.com/api",
      "description": "Production API"
    }
  ],
  "security": [
    {
      "MobileBearer": []
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "summary": "API health check",
        "description": "Public endpoint. Does not require authentication.",
        "security": [],
        "responses": {
          "200": {
            "description": "API is reachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "summary": "OAuth-style client credentials token endpoint",
        "description": "Optional convenience endpoint. client_id is the mobile API key prefix and client_secret is the one-time secret created by scripts/create_mobile_api_key.php. The returned access_token is the same bearer token format accepted by private endpoints.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bearer token response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthTokenResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/v1/app/config": {
      "get": {
        "summary": "Mobile app configuration",
        "description": "Returns brand colors, contact info, coverage, service lines, regional phone numbers, and feature flags. The first-launch app should submit booking requests to features.booking_submission_endpoint.",
        "responses": {
          "200": {
            "description": "Brand, contact, and app configuration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppConfigResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/service-areas": {
      "get": {
        "summary": "Service counties and cities",
        "description": "Returns counties and mapped cities from the live service database.",
        "responses": {
          "200": {
            "description": "Service area data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceAreasResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/services": {
      "get": {
        "summary": "Mobile service catalog and service lookup",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "county_slug",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "city_slug",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Category slug or name, such as hvac, plumbing, or electrical.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "distinct",
            "in": "query",
            "description": "Set to 1 to deduplicate services for mobile catalog use. With grouped=1, SEO city/location variants are collapsed so each service appears once per category.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "grouped",
            "in": "query",
            "description": "Set to 1 with distinct=1 to include data.categories[], each containing deduplicated services for that category.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 300
            },
            "description": "For grouped mobile catalogs, the API fetches enough rows to deduplicate and return the full category catalog; the iOS app uses 500."
          }
        ],
        "responses": {
          "200": {
            "description": "Service search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "description": "Returns live services from the database. For mobile apps, call with distinct=1&grouped=1&limit=500 to receive one deduplicated service catalog grouped by category. The grouped response deduplicates SEO city/location variants so each service appears once per category."
      }
    },
    "/v1/pages/{slug}": {
      "get": {
        "summary": "Mobile CMS page content",
        "description": "Returns database-backed page content as mobile-safe text blocks. Supported slugs are about/about-us and privacy/privacy-policy.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Page slug: about, about-us, privacy, or privacy-policy.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page content",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/leads": {
      "post": {
        "summary": "Create a customer lead",
        "description": "Creates a local mobile_leads record and emails the office. Requires name plus email or phone.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LeadCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Lead created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadCreateResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/bookings/request": {
      "post": {
        "summary": "Request a service booking",
        "description": "Creates a local mobile_leads record, a mobile_booking_requests record, and emails the office. Safe to use before ServiceTitan credentials are configured.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Booking request received",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingRequestResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/servicetitan/status": {
      "get": {
        "summary": "ServiceTitan integration status",
        "description": "Shows whether server-side ServiceTitan credentials and booking mappings are configured, plus the ServiceTitan scope plan for current and future backend phases.",
        "responses": {
          "200": {
            "description": "Integration status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceTitanStatusResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/servicetitan/bookings": {
      "get": {
        "summary": "List ServiceTitan booking-provider bookings",
        "description": "Requires ServiceTitan credentials, SERVICETITAN_BOOKING_PROVIDER, and SERVICETITAN_DIRECT_BOOKING_ENABLED=1. Returns 503 until direct booking is enabled.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "createdOnOrAfter",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "modifiedOnOrAfter",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Raw ServiceTitan booking list data"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      },
      "post": {
        "summary": "Create a ServiceTitan CRM booking-provider booking",
        "description": "Requires ServiceTitan credentials, SERVICETITAN_BOOKING_PROVIDER, and SERVICETITAN_DIRECT_BOOKING_ENABLED=1. The app should use /v1/bookings/request until direct booking is enabled.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceTitanBookingCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Raw ServiceTitan booking create response"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "MobileBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Use Authorization: Bearer <key_prefix>.<secret>. Create keys with scripts/create_mobile_api_key.php."
      }
    },
    "responses": {
      "ApiError": {
        "description": "Standard API error response",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "OAuthError": {
        "description": "OAuth-style error response",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OAuthErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "ok",
          "request_id"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "HealthResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "example": "healthy"
                  },
                  "service": {
                    "type": "string",
                    "example": "asuperior-mobile-api"
                  },
                  "version": {
                    "type": "string",
                    "example": "1.0.0"
                  },
                  "time": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        ]
      },
      "OAuthTokenRequest": {
        "type": "object",
        "required": [
          "grant_type",
          "client_id",
          "client_secret"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "client_credentials"
            ]
          },
          "client_id": {
            "type": "string",
            "description": "Mobile API key prefix, for example asm_live_abc123."
          },
          "client_secret": {
            "type": "string",
            "description": "Mobile API key secret, shown once by scripts/create_mobile_api_key.php."
          },
          "scope": {
            "type": "string"
          }
        }
      },
      "OAuthTokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "example": "Bearer"
          },
          "scope": {
            "type": "string"
          }
        }
      },
      "AppConfigResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "app": {
                    "type": "object"
                  },
                  "brand": {
                    "type": "object"
                  },
                  "coverage": {
                    "type": "object"
                  },
                  "service_lines": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "features": {
                    "$ref": "#/components/schemas/AppFeatureFlags"
                  }
                }
              }
            }
          }
        ]
      },
      "AppFeatureFlags": {
        "type": "object",
        "properties": {
          "service_search": {
            "type": "boolean"
          },
          "service_catalog": {
            "type": "boolean",
            "description": "True when mobile clients should use the grouped service catalog instead of a search-first interface."
          },
          "lead_capture": {
            "type": "boolean"
          },
          "booking_requests": {
            "type": "boolean"
          },
          "servicetitan_enabled": {
            "type": "boolean",
            "description": "True only when direct ServiceTitan booking-provider submission is available."
          },
          "direct_servicetitan_booking_enabled": {
            "type": "boolean"
          },
          "booking_submission_endpoint": {
            "type": "string",
            "enum": [
              "/bookings/request"
            ],
            "description": "Endpoint path, relative to /api/v1, that the iOS app should use for booking requests."
          },
          "servicetitan_forwarding_enabled": {
            "type": "boolean"
          },
          "first_launch_booking_mode": {
            "type": "string",
            "enum": [
              "local_booking_request",
              "local_booking_request_with_servicetitan_forward"
            ]
          }
        }
      },
      "ServiceAreasResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "counties": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/County"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "County": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "cities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/City"
            }
          }
        }
      },
      "City": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          }
        }
      },
      "ServicesResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "services": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ServiceResult"
                    }
                  },
                  "categories": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ServiceCategoryResult"
                    }
                  },
                  "count": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        ]
      },
      "ServiceResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "tagline": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon_class": {
            "type": "string"
          },
          "image_url": {
            "type": "string",
            "format": "uri"
          },
          "hero_image_url": {
            "type": "string",
            "format": "uri"
          },
          "is_featured": {
            "type": "boolean"
          },
          "show_on_home": {
            "type": "boolean"
          },
          "category": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "icon_class": {
                "type": "string"
              }
            }
          },
          "location": {
            "type": "object",
            "properties": {
              "city": {
                "type": "string"
              },
              "city_slug": {
                "type": "string"
              },
              "county": {
                "type": "string"
              },
              "county_slug": {
                "type": "string"
              }
            }
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Address": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "default": "FL"
          },
          "zip": {
            "type": "string"
          }
        }
      },
      "LeadCreateRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "service_line": {
            "type": "string",
            "enum": [
              "hvac",
              "plumbing",
              "electrical"
            ]
          },
          "service_slug": {
            "type": "string"
          },
          "county_slug": {
            "type": "string"
          },
          "city_slug": {
            "type": "string"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "message": {
            "type": "string"
          },
          "preferred_date": {
            "type": "string",
            "format": "date"
          },
          "preferred_time_window": {
            "type": "string"
          }
        },
        "description": "Either phone or email is required by the server."
      },
      "LeadCreateResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "lead_id": {
                    "type": "integer"
                  },
                  "status": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          }
        ]
      },
      "BookingRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/LeadCreateRequest"
          },
          {
            "type": "object",
            "properties": {
              "requested_start": {
                "type": "string",
                "format": "date-time"
              },
              "requested_end": {
                "type": "string",
                "format": "date-time"
              },
              "timezone": {
                "type": "string",
                "default": "America/Chicago"
              },
              "notes": {
                "type": "string"
              }
            }
          }
        ]
      },
      "BookingRequestResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "lead_id": {
                    "type": "integer"
                  },
                  "booking_request_id": {
                    "type": "integer"
                  },
                  "status": {
                    "type": "string",
                    "example": "requested"
                  },
                  "message": {
                    "type": "string"
                  },
                  "servicetitan_enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        ]
      },
      "ServiceTitanStatusResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "configured": {
                    "type": "boolean"
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "integration",
                      "production"
                    ]
                  },
                  "app_id_configured": {
                    "type": "boolean"
                  },
                  "client_credentials_configured": {
                    "type": "boolean"
                  },
                  "app_key_configured": {
                    "type": "boolean"
                  },
                  "tenant_configured": {
                    "type": "boolean"
                  },
                  "booking_provider_configured": {
                    "type": "boolean"
                  },
                  "booking_provider_tag_configured": {
                    "type": "boolean"
                  },
                  "direct_booking_enabled": {
                    "type": "boolean"
                  },
                  "direct_booking_env_enabled": {
                    "type": "boolean"
                  },
                  "default_job_mapping_configured": {
                    "type": "boolean"
                  },
                  "scope_plan": {
                    "type": "object",
                    "properties": {
                      "active_now": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/ServiceTitanScopePlanItem"
                        }
                      },
                      "optional_later": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/ServiceTitanScopePlanItem"
                        }
                      },
                      "direct_job_phase": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/ServiceTitanScopePlanItem"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "ServiceTitanScopePlanItem": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string"
          },
          "used_by": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "implemented",
              "available_if_required",
              "not_implemented"
            ]
          }
        }
      },
      "ServiceTitanBookingCreateRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BookingRequest"
          },
          {
            "type": "object",
            "properties": {
              "summary": {
                "type": "string"
              }
            }
          }
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object"
              }
            }
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "OAuthErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "ServiceCategoryResult": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon_class": {
            "type": "string"
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceResult"
            }
          }
        }
      },
      "ContentBlock": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "heading",
              "paragraph",
              "list"
            ]
          },
          "text": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "MobilePage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "body_text": {
            "type": "string"
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContentBlock"
            }
          },
          "updated_at": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PageResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "page": {
                    "$ref": "#/components/schemas/MobilePage"
                  }
                }
              }
            }
          }
        ]
      }
    }
  }
}
