{
  "openapi": "3.1.0",
  "info": {
    "title": "Gathrr venue and stall inventory API",
    "version": "1.0.0",
    "description": "Event-owned venue metadata and review-gated booth inventory. Floor-plan bytes and coordinates are not stored by these endpoints."
  },
  "servers": [{ "url": "/api/v1" }],
  "paths": {
    "/organizations/{organization_id}/events/{event_id}/venue": {
      "parameters": [{ "$ref": "#/components/parameters/OrganizationId" }, { "$ref": "#/components/parameters/EventId" }],
      "get": { "operationId": "getEventVenue", "security": [{ "BrowserSession": [] }], "responses": { "200": { "description": "Authorized event venue metadata" }, "404": { "description": "Venue not configured or event not accessible" } } },
      "put": { "operationId": "saveEventVenue", "security": [{ "BrowserSession": [], "CsrfToken": [] }], "description": "Requires venue.manage. Uses optimistic versioning. asset_url must be HTTPS; this API does not upload the file.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VenueInput" } } } }, "responses": { "200": { "description": "Venue metadata saved" }, "409": { "description": "Version conflict" }, "422": { "description": "Invalid halls or floor-plan metadata" } } }
    },
    "/organizations/{organization_id}/events/{event_id}/stalls/import": {
      "parameters": [{ "$ref": "#/components/parameters/OrganizationId" }, { "$ref": "#/components/parameters/EventId" }],
      "post": { "operationId": "importStallLabels", "security": [{ "BrowserSession": [], "CsrfToken": [] }], "description": "Requires venue.manage. Up to 500 rows per request. Imports are idempotent and new stalls remain needs_review with unknown availability.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StallImport" } } } }, "responses": { "201": { "description": "Import accepted" }, "409": { "description": "Venue or floor-plan revision mismatch" }, "422": { "description": "Unknown hall or duplicate stall code" } } }
    },
    "/organizations/{organization_id}/events/{event_id}/stalls/{code}/verify": {
      "parameters": [{ "$ref": "#/components/parameters/OrganizationId" }, { "$ref": "#/components/parameters/EventId" }, { "name": "code", "in": "path", "required": true, "schema": { "type": "string", "maxLength": 40 } }],
      "post": { "operationId": "verifyStallAvailability", "security": [{ "BrowserSession": [], "CsrfToken": [] }], "description": "Requires stall.allocate. Records the operator and a transactional audit record; repeat of the same state is idempotent.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "required": ["availability"], "properties": { "availability": { "enum": ["available", "tentative", "sold", "blocked"] } } } } } }, "responses": { "200": { "description": "Verified stall state" }, "404": { "description": "Stall not found or event inaccessible" }, "409": { "description": "Concurrent review update" } } }
    },
    "/public/events/{slug}/venue": {
      "get": { "operationId": "getPublicEventVenue", "security": [], "parameters": [{ "$ref": "#/components/parameters/Slug" }], "responses": { "200": { "description": "Published venue, halls and floor-plan metadata; asset_url is optional" }, "404": { "description": "Event or venue not available" } } }
    },
    "/public/events/{slug}/stalls": {
      "get": { "operationId": "listPublicVerifiedStalls", "security": [], "parameters": [{ "$ref": "#/components/parameters/Slug" }, { "name": "hall_code", "in": "query", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } }, { "name": "cursor", "in": "query", "schema": { "type": "string", "maxLength": 40 } }], "responses": { "200": { "description": "Verified stall inventory only, cursor paginated" } } }
    }
  },
  "components": {
    "securitySchemes": { "BrowserSession": { "type": "apiKey", "in": "cookie", "name": "gathrr_access" }, "CsrfToken": { "type": "apiKey", "in": "header", "name": "X-CSRF-Token" } },
    "parameters": { "OrganizationId": { "name": "organization_id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }, "EventId": { "name": "event_id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }, "Slug": { "name": "slug", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" } } },
    "schemas": {
      "VenueInput": { "type": "object", "additionalProperties": false, "required": ["name", "city", "halls", "floorplan"], "properties": { "version": { "type": "integer", "minimum": 0 }, "name": { "type": "string", "minLength": 2, "maxLength": 160 }, "city": { "type": "string", "minLength": 2, "maxLength": 80 }, "halls": { "type": "array", "minItems": 1, "maxItems": 30, "items": { "type": "object", "additionalProperties": false, "required": ["code", "name"], "properties": { "code": { "type": "string", "pattern": "^hall-[a-z0-9-]{1,20}$" }, "name": { "type": "string", "maxLength": 80 }, "layout_verification": { "const": "review_required" } } } }, "floorplan": { "type": "object", "additionalProperties": false, "required": ["file_name", "media_type", "sha256", "pages", "page_size"], "properties": { "file_name": { "type": "string", "maxLength": 180 }, "media_type": { "const": "application/pdf" }, "sha256": { "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "pages": { "type": "integer", "minimum": 1, "maximum": 50 }, "page_size": { "type": "string", "maxLength": 40 }, "asset_url": { "type": "string", "format": "uri", "pattern": "^https://" } } } } },
      "StallImport": { "type": "object", "additionalProperties": false, "required": ["source_sha256", "stalls"], "properties": { "source_sha256": { "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "stalls": { "type": "array", "minItems": 1, "maxItems": 500, "items": { "type": "object", "additionalProperties": false, "required": ["code", "hall_code", "availability"], "properties": { "code": { "type": "string", "maxLength": 40 }, "hall_code": { "type": "string", "pattern": "^hall-[a-z0-9-]{1,20}$" }, "availability": { "const": "unknown" } } } } } }
    }
  }
}
