{
  "openapi": "3.1.0",
  "info": {
    "title": "Hire Local Crew — Agent API",
    "version": "1.0.0",
    "description": "Agent-callable API for discovering and quoting local home-service providers across the U.S. Southeast. Read endpoints are public; quote requests can include an API key for attribution. Bookings are reserved for Phase B.",
    "contact": {
      "email": "agents@hirelocalcrew.com",
      "url": "https://hirelocalcrew.com/agents"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://hirelocalcrew.com/api/agent/v1",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key obtained at /agents. Optional for read endpoints — auth raises rate limits and adds attribution. Required for /bookings (Phase B)."
      }
    },
    "schemas": {
      "Service": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "example": "tree-removal"
          },
          "name": {
            "type": "string",
            "example": "Tree Removal"
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "provider_count": {
            "type": "integer"
          }
        }
      },
      "Location": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "example": "raleigh"
          },
          "name": {
            "type": "string",
            "example": "Raleigh"
          },
          "state": {
            "type": "string",
            "example": "NC"
          },
          "cities": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "zip": {
                  "type": "string",
                  "nullable": true
                },
                "population": {
                  "type": "integer",
                  "nullable": true
                },
                "lat": {
                  "type": "number",
                  "nullable": true
                },
                "lng": {
                  "type": "number",
                  "nullable": true
                }
              }
            }
          },
          "service_count": {
            "type": "integer"
          }
        }
      },
      "ProviderSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "example": "tree-removal/raleigh"
          },
          "service": {
            "$ref": "#/components/schemas/Service"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "brand_name": {
            "type": "string"
          },
          "has_active_provider": {
            "type": "boolean"
          },
          "active_provider_name": {
            "type": "string",
            "nullable": true
          },
          "rating": {
            "type": "object",
            "nullable": true,
            "properties": {
              "avg": {
                "type": "number"
              },
              "count": {
                "type": "integer"
              }
            }
          }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "required": [
          "provider_id",
          "consumer_name",
          "consumer_phone",
          "job_description"
        ],
        "properties": {
          "provider_id": {
            "type": "string",
            "description": "ID from /providers"
          },
          "consumer_name": {
            "type": "string",
            "minLength": 2
          },
          "consumer_phone": {
            "type": "string",
            "description": "E.164 preferred"
          },
          "consumer_email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "consumer_zip": {
            "type": "string",
            "pattern": "^\\d{5}$",
            "nullable": true
          },
          "job_description": {
            "type": "string",
            "minLength": 10,
            "maxLength": 2000
          },
          "preferred_timing": {
            "type": "string",
            "description": "Free-form timing preference (e.g. 'this weekend', 'within 2 weeks')",
            "nullable": true
          },
          "agent_context": {
            "type": "object",
            "description": "Optional agent metadata for attribution and analytics",
            "properties": {
              "conversation_id": {
                "type": "string"
              },
              "session_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "QuoteAck": {
        "type": "object",
        "properties": {
          "quote_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "received"
            ]
          },
          "estimated_response_time_minutes": {
            "type": "integer"
          },
          "provider_name": {
            "type": "string"
          },
          "tracking_url": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/services": {
      "get": {
        "summary": "List all service categories in the network",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "Service catalog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/locations": {
      "get": {
        "summary": "List all city groups + cities the network covers",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "Location catalog"
          }
        }
      }
    },
    "/providers": {
      "get": {
        "summary": "Search providers by service + location + ZIP",
        "tags": [
          "Providers"
        ],
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Service slug"
          },
          {
            "name": "location",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "City-group slug"
          },
          {
            "name": "zip",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}$"
            }
          },
          {
            "name": "has_rented",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Only providers with an active contractor fulfilling"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Provider list"
          }
        }
      }
    },
    "/providers/{id}": {
      "get": {
        "summary": "Full detail on a single provider",
        "tags": [
          "Providers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Provider detail"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/providers/{id}/availability": {
      "get": {
        "summary": "Default weekly availability windows for a provider",
        "tags": [
          "Providers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Availability"
          }
        }
      }
    },
    "/quote": {
      "post": {
        "summary": "Submit a quote request from an agent on behalf of a consumer",
        "description": "Creates a Lead with source=agent. The provider receives the lead within seconds via email + SMS notification. Returns a tracking URL the consumer can visit to follow up. Idempotent on (provider_id, consumer_phone, job_description) within a 1-hour window.",
        "tags": [
          "Quotes"
        ],
        "security": [
          {
            "bearer": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote received",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteAck"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/bookings": {
      "post": {
        "summary": "Book a service slot with escrow (Phase B — not yet active)",
        "tags": [
          "Bookings"
        ],
        "responses": {
          "501": {
            "description": "Not implemented — see body for status + future ETA"
          }
        }
      }
    }
  }
}