{
  "openapi": "3.1.0",
  "info": {
    "title": "Fifty Asks booking API",
    "version": "1.0.0",
    "description": "Book a free Fifty Asks check or a free 15-minute call for a local service business. Agents are welcome; no auth, no CAPTCHA, only spambot deterrents (rate limit per network, a mail-domain check). A check booked by an agent is delivered in 2 business days instead of 5. Status 'booked' or 'confirmed' is only returned when it is true."
  },
  "servers": [{ "url": "https://fiftyasks.com" }],
  "paths": {
    "/api/book": {
      "post": {
        "operationId": "book",
        "summary": "Book the free check (service=check) or a 15-minute call (service=call)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingRequest" },
          "example": { "service": "check", "business": "Acme Plumbing", "trade": "plumber", "city": "Round Rock, TX", "contact_name": "Pat Lee", "email": "pat@example.com", "booked_by": "agent", "agent_name": "ExampleAgent 1.0" } } } },
        "responses": {
          "201": { "description": "Booking created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } } },
          "200": { "description": "Same booking already existed (returned, nothing new created)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } } },
          "422": { "description": "Missing or invalid fields (listed in 'missing')" },
          "409": { "description": "Call slot not available" },
          "429": { "description": "Too many requests from this network this hour" }
        }
      },
      "get": {
        "operationId": "bookViaLink",
        "summary": "For agents that can only GET: pass the same fields as query parameters to get a confirm_url; a GET on confirm_url places the booking",
        "parameters": [
          { "name": "service", "in": "query", "schema": { "type": "string", "enum": ["check", "call"] } },
          { "name": "business", "in": "query", "schema": { "type": "string" } },
          { "name": "trade", "in": "query", "schema": { "type": "string" } },
          { "name": "city", "in": "query", "schema": { "type": "string" } },
          { "name": "contact_name", "in": "query", "schema": { "type": "string" } },
          { "name": "email", "in": "query", "schema": { "type": "string" } },
          { "name": "phone", "in": "query", "schema": { "type": "string" } },
          { "name": "start", "in": "query", "schema": { "type": "string" } },
          { "name": "website", "in": "query", "schema": { "type": "string" } },
          { "name": "notes", "in": "query", "schema": { "type": "string" } },
          { "name": "agent_name", "in": "query", "schema": { "type": "string" } },
          { "name": "booked_by", "in": "query", "schema": { "type": "string", "enum": ["agent", "person"] } },
          { "name": "confirm", "in": "query", "schema": { "type": "string" }, "description": "Signature from the preview's confirm_url" }
        ],
        "responses": { "200": { "description": "Preview with confirm_url (nothing booked yet)" }, "201": { "description": "Booked (when confirm is present and valid)" } }
      }
    },
    "/api/slots": {
      "get": {
        "operationId": "listCallSlots",
        "summary": "Open start times for the 15-minute call (ISO 8601, Central Time)",
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 14, "default": 7 } }],
        "responses": { "200": { "description": "Slots", "content": { "application/json": { "schema": { "type": "object", "properties": { "slots": { "type": "array", "items": { "type": "string" } }, "timezone": { "type": "string" } } } } } } }
      }
    },
    "/api/bookings/{id}": {
      "get": {
        "operationId": "getBooking",
        "summary": "Current status of a booking",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^FA-[A-Z0-9]{8}$" } }],
        "responses": { "200": { "description": "Booking", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } } }, "404": { "description": "Unknown id" } }
      }
    },
    "/api/message": {
      "post": {
        "operationId": "sendMessage",
        "summary": "Message the Fifty Asks team; a person replies by email",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["message"],
          "properties": { "message": { "type": "string" }, "reply_to": { "type": "string", "format": "email" }, "on_behalf_of": { "type": "string" }, "agent_name": { "type": "string" } } } } } },
        "responses": { "201": { "description": "Received" } }
      }
    }
  },
  "components": {
    "schemas": {
      "BookingRequest": {
        "type": "object",
        "required": ["service", "business", "contact_name", "email"],
        "properties": {
          "service": { "type": "string", "enum": ["check", "call"] },
          "business": { "type": "string", "description": "Business name" },
          "trade": { "type": "string", "description": "Required for check: plumber, HVAC, roofer..." },
          "city": { "type": "string", "description": "Required for check: town and state served" },
          "website": { "type": "string" },
          "contact_name": { "type": "string", "description": "The person the booking is for" },
          "email": { "type": "string", "format": "email", "description": "Where the report or calendar invite goes" },
          "phone": { "type": "string", "description": "Required for call" },
          "start": { "type": "string", "description": "Required for call: a slot from /api/slots" },
          "notes": { "type": "string" },
          "agent_name": { "type": "string", "description": "Which agent is booking" },
          "booked_by": { "type": "string", "enum": ["agent", "person"], "default": "agent" }
        }
      },
      "Booking": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "booking_id": { "type": "string" },
          "service": { "type": "string" },
          "status": { "type": "string", "enum": ["booked", "waitlisted", "confirmed", "pending", "cancelled"] },
          "deliver_by": { "type": "string", "format": "date" },
          "call": { "type": "object" },
          "what_happens_next": { "type": "string" },
          "status_url": { "type": "string" }
        }
      }
    }
  }
}
