{
  "openapi": "3.1.0",
  "info": {
    "title": "Work From Austin API",
    "version": "1.0.0",
    "description": "Public HTTP endpoints for Work From Austin, a curated directory of remote-work-friendly venues in Austin, Texas. These are the write/lookup endpoints used by the site's suggestion and notification flows. The full read dataset is published as static files: /llms.txt (index), /llms-full.txt (every venue, machine-parseable), /data/venues.json, and /sitemap.xml. Agent usage rules: /AGENTS.md.",
    "contact": { "url": "https://www.workfromaustin.com/" },
    "license": { "name": "Content reusable with attribution; see /AGENTS.md" }
  },
  "servers": [{ "url": "https://www.workfromaustin.com", "description": "Production" }],
  "tags": [
    { "name": "venues", "description": "Venue lookup and community suggestions" },
    { "name": "notifications", "description": "Email notification list" }
  ],
  "paths": {
    "/api/venues/lookup": {
      "post": {
        "tags": ["venues"],
        "summary": "Look up a venue from a Google Maps URL",
        "description": "Resolves a Google Maps place URL to structured venue fields (name, type, neighborhood, hours) for prefilling a suggestion. Rate limited to 5 requests per hour per IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LookupRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lookup result. `venue` is null when the request looks automated.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LookupResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/venues/suggest": {
      "post": {
        "tags": ["venues"],
        "summary": "Submit a venue suggestion",
        "description": "Queues a community venue suggestion for human review. Rate limited to 2 per hour and 5 per day per IP. Submissions are AI-classified, then approved or rejected by a human editor.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SuggestRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suggestion accepted for review.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SuggestResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/subscribe": {
      "post": {
        "tags": ["notifications"],
        "summary": "Subscribe an email to the guides notification list",
        "description": "Stores an email address for the guides notification list. Duplicate emails silently succeed to prevent address enumeration. Rate limited per IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SubscribeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscribed (or already subscribed).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Ok" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "405": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "Request error. JSON body with a human-readable message.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded for this IP.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      }
    },
    "schemas": {
      "Ok": {
        "type": "object",
        "properties": { "ok": { "type": "boolean", "const": true } },
        "required": ["ok"]
      },
      "Error": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "const": false },
          "error": { "type": "string", "description": "Human-readable error message." }
        },
        "required": ["error"]
      },
      "LookupRequest": {
        "type": "object",
        "properties": {
          "url": { "type": "string", "format": "uri", "description": "A Google Maps place URL." }
        },
        "required": ["url"]
      },
      "LookupResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "venue": {
            "type": ["object", "null"],
            "properties": {
              "name": { "type": "string" },
              "type": { "type": "string", "description": "One of: cafe, coffee_shop, bakery, bar, brewery, restaurant, library, tea_house, athletic." },
              "neighborhood": { "type": "string" },
              "googleMapsUrl": { "type": "string", "format": "uri" }
            }
          }
        },
        "required": ["ok"]
      },
      "SuggestRequest": {
        "type": "object",
        "properties": {
          "url": { "type": "string", "format": "uri", "description": "A Google Maps place URL." },
          "name": { "type": "string" },
          "type": { "type": "string" },
          "neighborhood": { "type": "string" }
        },
        "required": ["url"]
      },
      "SuggestResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "message": { "type": "string" }
        },
        "required": ["ok"]
      },
      "SubscribeRequest": {
        "type": "object",
        "properties": {
          "email": { "type": "string", "format": "email" }
        },
        "required": ["email"]
      }
    }
  }
}
