{"openapi":"3.0.3","info":{"title":"NOPOS API","description":"The NOPOS API provides a comprehensive set of RESTful endpoints for managing business operations data, including organization/location operations (legacy `stores` endpoints), products, inventory, customers, orders, payments, and generic content management.\n\n## Features\n- **Multi-tenant Architecture**: Each API key is associated with a specific location (stored as `store_id` for compatibility)\n- **JWT-based Authentication**: Secure authentication with Row Level Security (RLS)\n- **Comprehensive Business Management**: Products, services, customers, orders, payments, bookings\n- **Content Management**: Generic content for websites and applications\n- **Professional Management**: Healthcare and service provider management\n\n## Canonical Core Terminology\nThe canonical core resources remain:\n- `customer` via `/v1/customers`\n- `booking` via `/v1/bookings`\n\nIndustry adapters and docs may translate terms without creating vertical-specific storage:\n\n| Canonical Core | Healthcare Alias | Restaurant Alias | Salon/Wellness Alias |\n| --- | --- | --- | --- |\n| customer | patient | guest | client |\n| booking | appointment | reservation | appointment |\n\nAlias paths such as `/patients` or `/reservations` should be treated as adapter/documentation overlays only and map to the canonical core resources above.\n \n## Authentication\nAll endpoints require an API key to be provided in the `X-API-Key` header. Each API key is associated with a specific location, and all operations are automatically scoped to that location (`store_id` in current schema).\n\n## Base URLs\n- **Production**: `https://nopos.vercel.app/v1`\n- **Development**: `http://localhost:3000/v1`\n\n## API Versioning\nAll API endpoints are versioned using URL path versioning. The current version is `v1`.\nFuture versions will be available at `/v2`, `/v3`, etc.\n\n## Pagination\nList endpoints support pagination using the following query parameters:\n- `limit`: Number of items per page (default: 20, max: 100)\n- `offset`: Number of items to skip (default: 0)\n- `page`: Page number (1-indexed, default: 1)\n\nExample: `GET /v1/products?limit=20&page=2`\n","version":"1.1.0","contact":{"name":"NOPOS API Support","email":"support@nopos.com"},"license":{"name":"ISC","url":"https://opensource.org/licenses/ISC"}},"servers":[{"url":"https://nopos.vercel.app/v1","description":"Production server (API v1)"},{"url":"http://localhost:3000/v1","description":"Development server (API v1)"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for location authentication (legacy store-scoped key)"}},"parameters":{"LimitParam":{"name":"limit","in":"query","description":"Number of items to return per page","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"example":20},"OffsetParam":{"name":"offset","in":"query","description":"Number of items to skip","schema":{"type":"integer","minimum":0,"default":0},"example":0},"PageParam":{"name":"page","in":"query","description":"Page number (1-indexed)","schema":{"type":"integer","minimum":1,"default":1},"example":1}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Error message"},"details":{"type":"string","description":"Additional error details"}},"required":["error"]},"PaginationMetadata":{"type":"object","description":"Pagination metadata for list responses","properties":{"total":{"type":"integer","description":"Total number of items in the dataset","example":150},"count":{"type":"integer","description":"Number of items in the current page","example":20},"page":{"type":"integer","description":"Current page number (1-indexed)","example":1},"pages":{"type":"integer","description":"Total number of pages","example":8},"limit":{"type":"integer","description":"Number of items per page","example":20},"offset":{"type":"integer","description":"Number of items skipped","example":0},"hasNext":{"type":"boolean","description":"Whether there is a next page","example":true},"hasPrev":{"type":"boolean","description":"Whether there is a previous page","example":false}}},"JsonObject":{"type":"object","description":"Explicit flexible JSON object for module-specific payloads, settings, or metadata.","additionalProperties":true},"ConsentSignature":{"type":"object","description":"An append-only consent signature record linked to a patient and optionally an encounter.","properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"clinic_id":{"type":"string","format":"uuid"},"patient_id":{"type":"string","format":"uuid"},"encounter_id":{"type":"string","format":"uuid","nullable":true},"consent_type":{"type":"string"},"template_id":{"type":"string","format":"uuid","nullable":true},"template_name":{"type":"string"},"template_version":{"type":"string"},"status":{"type":"string","enum":["captured","declined","revoked","expired"]},"signed_at":{"type":"string","format":"date-time","nullable":true},"signed_by_user_id":{"type":"string","format":"uuid","nullable":true},"witnessed_by_user_id":{"type":"string","format":"uuid","nullable":true},"ip_address":{"type":"string","nullable":true},"user_agent":{"type":"string","nullable":true},"signature_method":{"type":"string"},"signature_payload":{"$ref":"#/components/schemas/JsonObject"},"document_id":{"type":"string","format":"uuid","nullable":true},"metadata":{"$ref":"#/components/schemas/JsonObject"},"created_by_user_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"ConsentSignatureCreate":{"type":"object","required":["patient_id","consent_type","template_name","template_version"],"properties":{"patient_id":{"type":"string","format":"uuid"},"encounter_id":{"type":"string","format":"uuid"},"consent_type":{"type":"string"},"template_id":{"type":"string","format":"uuid"},"template_name":{"type":"string"},"template_version":{"type":"string"},"status":{"type":"string","enum":["captured","declined","revoked","expired"],"default":"captured"},"signed_at":{"type":"string","format":"date-time"},"signed_by_user_id":{"type":"string","format":"uuid"},"witnessed_by_user_id":{"type":"string","format":"uuid"},"ip_address":{"type":"string"},"user_agent":{"type":"string"},"signature_method":{"type":"string","default":"electronic"},"signature_payload":{"$ref":"#/components/schemas/JsonObject"},"document_id":{"type":"string","format":"uuid"},"metadata":{"$ref":"#/components/schemas/JsonObject"}}},"MedicalHistoryRecord":{"type":"object","description":"A patient medical history fact such as a condition, allergy, surgery, or note.","properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"clinic_id":{"type":"string","format":"uuid"},"patient_id":{"type":"string","format":"uuid"},"encounter_id":{"type":"string","format":"uuid","nullable":true},"category":{"type":"string","enum":["condition","allergy","medication","surgery","family_history","social_history","contraindication","note"]},"title":{"type":"string"},"details":{"type":"string","nullable":true},"status":{"type":"string","enum":["active","resolved","inactive","entered_in_error"]},"severity":{"type":"string","enum":["low","medium","high","critical"],"nullable":true},"onset_date":{"type":"string","format":"date","nullable":true},"resolved_at":{"type":"string","format":"date-time","nullable":true},"source":{"type":"string"},"metadata":{"$ref":"#/components/schemas/JsonObject"},"created_by_user_id":{"type":"string","format":"uuid","nullable":true},"updated_by_user_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"MedicalHistoryCreate":{"type":"object","required":["patient_id","category","title"],"properties":{"patient_id":{"type":"string","format":"uuid"},"encounter_id":{"type":"string","format":"uuid"},"category":{"type":"string","enum":["condition","allergy","medication","surgery","family_history","social_history","contraindication","note"]},"title":{"type":"string"},"details":{"type":"string"},"status":{"type":"string","enum":["active","resolved","inactive","entered_in_error"],"default":"active"},"severity":{"type":"string","enum":["low","medium","high","critical"]},"onset_date":{"type":"string","format":"date"},"resolved_at":{"type":"string","format":"date-time"},"source":{"type":"string","default":"staff"},"metadata":{"$ref":"#/components/schemas/JsonObject"}}},"MedicalHistoryUpdate":{"type":"object","description":"Partial update for a medical history record. patient_id cannot be changed.","properties":{"encounter_id":{"type":"string","format":"uuid","nullable":true},"category":{"type":"string","enum":["condition","allergy","medication","surgery","family_history","social_history","contraindication","note"]},"title":{"type":"string"},"details":{"type":"string","nullable":true},"status":{"type":"string","enum":["active","resolved","inactive","entered_in_error"]},"severity":{"type":"string","enum":["low","medium","high","critical"],"nullable":true},"onset_date":{"type":"string","format":"date","nullable":true},"resolved_at":{"type":"string","format":"date-time","nullable":true},"source":{"type":"string"},"metadata":{"$ref":"#/components/schemas/JsonObject"}}},"PlatformEventType":{"type":"object","description":"A clinic-scoped event type catalog entry for the platform event bus.","properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"clinic_id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"description":{"type":"string","nullable":true},"producer":{"type":"string","nullable":true},"schema":{"$ref":"#/components/schemas/JsonObject"},"is_active":{"type":"boolean"},"metadata":{"$ref":"#/components/schemas/JsonObject"},"created_by_user_id":{"type":"string","format":"uuid","nullable":true},"updated_by_user_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PlatformEventTypeCreate":{"type":"object","required":["event_type"],"properties":{"event_type":{"type":"string","description":"Unique event type identifier within the clinic scope."},"description":{"type":"string"},"producer":{"type":"string"},"schema":{"$ref":"#/components/schemas/JsonObject"},"is_active":{"type":"boolean","default":true},"metadata":{"$ref":"#/components/schemas/JsonObject"}}},"PlatformEvent":{"type":"object","description":"An append-only platform event bus record.","properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"clinic_id":{"type":"string","format":"uuid"},"patient_id":{"type":"string","format":"uuid","nullable":true},"encounter_id":{"type":"string","format":"uuid","nullable":true},"event_type":{"type":"string"},"aggregate_type":{"type":"string","nullable":true},"aggregate_id":{"type":"string","format":"uuid","nullable":true},"payload":{"$ref":"#/components/schemas/JsonObject"},"metadata":{"$ref":"#/components/schemas/JsonObject"},"emitted_by_user_id":{"type":"string","format":"uuid","nullable":true},"emitted_at":{"type":"string","format":"date-time"},"processed_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"PlatformEventCreate":{"type":"object","required":["event_type"],"properties":{"event_type":{"type":"string","description":"Must match a registered event_type in platform_event_types for this clinic."},"patient_id":{"type":"string","format":"uuid"},"encounter_id":{"type":"string","format":"uuid"},"aggregate_type":{"type":"string"},"aggregate_id":{"type":"string","format":"uuid"},"payload":{"$ref":"#/components/schemas/JsonObject"},"metadata":{"$ref":"#/components/schemas/JsonObject"},"emitted_at":{"type":"string","format":"date-time"}}},"InventoryItem":{"type":"object","description":"An inventory item with par-level and depletion tracking fields.","properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"quantity_on_hand":{"type":"number","description":"Current quantity in stock"},"par_level":{"type":"number","description":"Minimum desired stock level; triggers depletion alert when crossed","default":0},"reorder_point":{"type":"number","description":"Quantity at which an auto-reorder should fire","default":0},"unit":{"type":"string","description":"Unit of measure (e.g. each, kg, liter)","default":"each"},"unit_cost":{"type":"number","nullable":true,"description":"Per-unit cost for cost-impact calculations"},"vendor_name":{"type":"string","nullable":true},"auto_reorder":{"type":"boolean","description":"When true the Hermes agent will trigger a vendor notification at reorder_point","default":false},"store_id":{"type":"string","format":"uuid","nullable":true},"updated_at":{"type":"string","format":"date-time"},"product":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"store_id":{"type":"string","format":"uuid"}}}}},"InventoryPatchRequest":{"type":"object","description":"Fields accepted by PATCH /inventory/:id. At least one field must be supplied.","properties":{"quantity_on_hand":{"type":"number"},"par_level":{"type":"number"},"reorder_point":{"type":"number"},"unit":{"type":"string"},"unit_cost":{"type":"number","nullable":true},"vendor_name":{"type":"string","nullable":true},"auto_reorder":{"type":"boolean"}}},"WasteLogCreateRequest":{"type":"object","required":["inventory_id","quantity_wasted"],"properties":{"inventory_id":{"type":"string","format":"uuid","description":"ID of the inventory item that was wasted"},"quantity_wasted":{"type":"number","description":"Amount wasted (must be positive)","minimum":0,"exclusiveMinimum":true},"reason":{"type":"string","enum":["spoilage","prep_error","over_portion","other"],"nullable":true},"logged_by":{"type":"string","nullable":true,"description":"Free-text name or ID of the staff member logging the event"}}},"WasteLogEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"inventory_id":{"type":"string","format":"uuid"},"quantity_wasted":{"type":"number"},"reason":{"type":"string","enum":["spoilage","prep_error","over_portion","other"],"nullable":true},"cost_impact":{"type":"number","nullable":true,"description":"Server-computed quantity_wasted * unit_cost at insert time"},"logged_by":{"type":"string","nullable":true},"logged_at":{"type":"string","format":"date-time"}}},"WasteSummaryRow":{"type":"object","description":"Daily waste aggregated by reason.","properties":{"date":{"type":"string","format":"date","description":"Reporting day (YYYY-MM-DD derived from logged_at)"},"reason":{"type":"string","enum":["spoilage","prep_error","over_portion","other"],"nullable":true},"total_cost":{"type":"number","description":"Sum of cost_impact for the day and reason"},"total_quantity":{"type":"number","description":"Sum of quantity_wasted for the day and reason"},"event_count":{"type":"integer","description":"Number of waste events in this bucket"}}},"PaginatedGenericResponse":{"type":"object","description":"Standard paginated response wrapper for list endpoints.","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/JsonObject"}},"pagination":{"$ref":"#/components/schemas/PaginationMetadata"}},"required":["data","pagination"]},"RateLimitError":{"type":"object","properties":{"error":{"type":"string","description":"Error message"},"remaining":{"type":"integer","description":"Requests remaining in the active rate-limit window"},"resetTime":{"type":"integer","description":"Unix epoch milliseconds when the active rate-limit window resets"}},"required":["error"]},"AIContentGenerationRequest":{"type":"object","properties":{"prompt":{"type":"string","description":"Prompt to generate text content from"},"systemPrompt":{"type":"string","description":"Optional system prompt override"},"model":{"type":"string","default":"anthropic/claude-haiku-4.5"},"maxTokens":{"type":"integer","minimum":1,"default":500},"temperature":{"type":"number","minimum":0,"maximum":2,"default":0.7}},"required":["prompt"]},"AIContentGenerationResponse":{"type":"object","properties":{"content":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string","enum":["openrouter","openai"]},"usage":{"$ref":"#/components/schemas/JsonObject"}},"required":["content","model","provider"]},"AISlideGenerationRequest":{"type":"object","properties":{"prompt":{"type":"string","description":"Prompt to generate slide copy from"},"model":{"type":"string","default":"anthropic/claude-haiku-4.5"}},"required":["prompt"]},"AISlideGenerationResponse":{"type":"object","properties":{"title":{"type":"string"},"subtitle":{"type":"string"},"description":{"type":"string"},"provider":{"type":"string","enum":["openrouter","openai"]}},"required":["title","provider"]},"AIImageGenerationRequest":{"type":"object","properties":{"prompt":{"type":"string","description":"Prompt to generate an image from"},"model":{"type":"string","default":"dall-e-3"},"size":{"type":"string","enum":["1024x1024","1792x1024","1024x1792"],"default":"1792x1024"},"quality":{"type":"string","enum":["standard","hd"],"default":"standard"},"style":{"type":"string","enum":["natural","vivid"],"default":"natural"},"alt":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"usage":{"type":"string"},"metadata":{"$ref":"#/components/schemas/JsonObject"}},"required":["prompt"]},"AIImageGenerationResponse":{"type":"object","properties":{"message":{"type":"string"},"data":{"$ref":"#/components/schemas/JsonObject"},"provider":{"type":"string","enum":["openrouter","openai"]}}},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"label":{"type":"string","nullable":true},"events":{"type":"array","items":{"type":"string"}},"secret":{"type":"string","nullable":true,"description":"Optional signing secret for outbound webhook delivery"},"is_active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time","nullable":true}},"required":["id","store_id","url","events","is_active"]},"WebhookInput":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"label":{"type":"string"},"events":{"type":"array","minItems":1,"items":{"type":"string"}},"secret":{"type":"string"}},"required":["url","events"]},"WebhookUpdate":{"type":"object","minProperties":1,"properties":{"url":{"type":"string","format":"uri"},"label":{"type":"string","nullable":true},"events":{"type":"array","minItems":1,"items":{"type":"string"}},"secret":{"type":"string","nullable":true},"is_active":{"type":"boolean"}}},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"webhook_id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"payload":{"$ref":"#/components/schemas/JsonObject"},"status":{"type":"string","enum":["delivered","failed"]},"http_status":{"type":"integer","nullable":true},"response_body":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"delivered_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"WebhookListResponse":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}},"required":["webhooks"]},"WebhookDeliveryListResponse":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}},"required":["deliveries"]},"WebhookTestResponse":{"type":"object","properties":{"success":{"type":"boolean"},"http_status":{"type":"integer","nullable":true},"response_body":{"type":"string"},"error":{"type":"string","nullable":true},"latency_ms":{"type":"integer"},"delivery":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["success","http_status","response_body","latency_ms"]},"TwilioWebhookAcknowledgement":{"type":"string","description":"TwiML XML acknowledgement returned to Twilio webhooks.","example":"<Response></Response>"},"Store":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique store identifier"},"name":{"type":"string","description":"Store name"},"description":{"type":"string","nullable":true,"description":"Store description"},"location":{"type":"string","nullable":true,"description":"Legacy location field"},"address":{"type":"string","nullable":true,"description":"Street address"},"city":{"type":"string","nullable":true,"description":"City"},"state":{"type":"string","nullable":true,"description":"State or province"},"zip_code":{"type":"string","nullable":true,"description":"ZIP or postal code"},"country":{"type":"string","default":"US","description":"Country code"},"phone":{"type":"string","nullable":true,"description":"Phone number"},"email":{"type":"string","format":"email","nullable":true,"description":"Email address"},"website":{"type":"string","format":"uri","nullable":true,"description":"Website URL"},"timezone":{"type":"string","default":"America/New_York","description":"Store timezone"},"business_hours":{"type":"object","nullable":true,"description":"Business hours configuration","additionalProperties":true},"settings":{"type":"object","nullable":true,"description":"Store-specific settings","additionalProperties":true},"is_active":{"type":"boolean","default":true,"description":"Whether the store is active"},"archived":{"type":"boolean","default":false,"description":"Whether the store is archived"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["id","name","created_at","updated_at"]},"StoreStats":{"type":"object","properties":{"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"statistics":{"type":"object","properties":{"products":{"type":"integer","description":"Number of products"},"customers":{"type":"integer","description":"Number of customers"},"orders":{"type":"integer","description":"Number of orders"},"bookings":{"type":"integer","description":"Number of bookings"}},"required":["products","customers","orders","bookings"]},"generated_at":{"type":"string","format":"date-time","description":"Statistics generation timestamp"}},"required":["store_id","statistics","generated_at"]},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique product identifier"},"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"name":{"type":"string","description":"Product name"},"sku":{"type":"string","nullable":true,"description":"Stock Keeping Unit"},"price":{"type":"number","format":"decimal","description":"Product price"},"description":{"type":"string","nullable":true,"description":"Product description"},"product_type":{"type":"string","enum":["product","service"],"default":"product","description":"Type of product"},"metadata":{"type":"object","nullable":true,"description":"Additional product metadata","additionalProperties":true},"archived":{"type":"boolean","default":false,"description":"Whether the product is archived"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["id","store_id","name","price","created_at","updated_at"]},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique customer identifier"},"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"first_name":{"type":"string","description":"Customer first name"},"last_name":{"type":"string","description":"Customer last name"},"email":{"type":"string","format":"email","description":"Customer email address"},"phone":{"type":"string","nullable":true,"description":"Customer phone number"},"address":{"type":"string","nullable":true,"description":"Customer address"},"city":{"type":"string","nullable":true,"description":"Customer city"},"state":{"type":"string","nullable":true,"description":"Customer state"},"zip_code":{"type":"string","nullable":true,"description":"Customer ZIP code"},"notes":{"type":"string","nullable":true,"description":"Customer notes"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["id","store_id","first_name","last_name","email","created_at","updated_at"]},"Order":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique order identifier"},"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"customer_id":{"type":"string","format":"uuid","description":"Customer identifier"},"status":{"type":"string","enum":["pending","confirmed","processing","completed","cancelled"],"default":"pending","description":"Order status"},"total_amount":{"type":"number","format":"decimal","description":"Total order amount"},"notes":{"type":"string","nullable":true,"description":"Order notes"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["id","store_id","customer_id","status","total_amount","created_at","updated_at"]},"Booking":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique booking identifier"},"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"customer_id":{"type":"string","format":"uuid","description":"Customer identifier"},"product_id":{"type":"string","format":"uuid","description":"Service/product identifier"},"professional_id":{"type":"string","format":"uuid","nullable":true,"description":"Professional identifier"},"booking_start_time":{"type":"string","format":"date-time","description":"Booking start time"},"booking_end_time":{"type":"string","format":"date-time","description":"Booking end time"},"status":{"type":"string","enum":["pending","confirmed","in_progress","completed","cancelled","no_show"],"default":"pending","description":"Booking status"},"notes":{"type":"string","nullable":true,"description":"Booking notes"},"amount":{"type":"number","format":"decimal","nullable":true,"description":"Booking amount"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["id","store_id","customer_id","product_id","booking_start_time","booking_end_time","status","created_at","updated_at"]},"Slide":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique slide identifier"},"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"content_type":{"type":"string","default":"slide","description":"Content type (always 'slide')"},"title":{"type":"string","description":"Slide title"},"body":{"type":"string","nullable":true,"description":"Slide content"},"status":{"type":"string","enum":["draft","published","archived"],"default":"draft","description":"Slide status"},"metadata":{"type":"object","nullable":true,"description":"Slide metadata","properties":{"image_url":{"type":"string","format":"uri","description":"URL of the slide image"},"background_color":{"type":"string","description":"Background color (hex)"},"text_color":{"type":"string","description":"Text color (hex)"},"font_size":{"type":"string","description":"Font size (CSS)"},"text_position":{"type":"string","enum":["top","center","bottom"]},"animation":{"type":"string","description":"Animation name"},"duration":{"type":"number","description":"Duration in seconds"},"order":{"type":"integer","description":"Display order"},"tags":{"type":"array","items":{"type":"string"}},"ai_generated":{"type":"boolean"},"prompt_used":{"type":"string"}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"published_at":{"type":"string","format":"date-time","nullable":true}},"required":["id","store_id","title","status","created_at","updated_at"]},"SlideInput":{"type":"object","properties":{"title":{"type":"string","description":"Slide title (max 255 chars)"},"body":{"type":"string","description":"Slide content (max 10,000 chars)"},"image_url":{"type":"string","format":"uri","description":"URL of the slide image"},"background_color":{"type":"string","description":"Background color (hex, e.g.,"},"text_color":{"type":"string","description":"Text color (hex, e.g.,"},"font_size":{"type":"string","description":"Font size (CSS, e.g., 24px, 1.5em)"},"text_position":{"type":"string","enum":["top","center","bottom"],"default":"center"},"animation":{"type":"string","description":"Animation name"},"duration":{"type":"number","description":"Duration in seconds (1-300)","default":5},"order":{"type":"integer","description":"Display order (0+)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags (max 20)"},"ai_generated":{"type":"boolean","default":false},"prompt_used":{"type":"string","description":"AI prompt used"},"status":{"type":"string","enum":["draft","published","archived"],"default":"draft"}},"required":["title"]},"ImageAsset":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique image identifier"},"store_id":{"type":"string","format":"uuid","description":"Store identifier"},"public_url":{"type":"string","format":"uri","description":"Public URL of the image"},"storage_path":{"type":"string","description":"Storage path in bucket"},"hash":{"type":"string","description":"SHA256 hash of the image"},"format":{"type":"string","enum":["jpg","png","webp","gif"],"description":"Image format"},"size":{"type":"integer","description":"File size in bytes"},"alt":{"type":"string","nullable":true,"description":"Alt text"},"tags":{"type":"array","nullable":true,"items":{"type":"string"}},"usage":{"type":"string","nullable":true,"description":"Usage context"},"metadata":{"type":"object","nullable":true,"description":"Additional metadata"},"persisted":{"type":"boolean","description":"Whether the image is persisted"},"status":{"type":"string","enum":["persisted","preview","archived"]},"version":{"type":"integer","description":"Version number"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","store_id","public_url","storage_path","hash","format","size","status","version","created_at","updated_at"]},"ImageAssetResponse":{"type":"object","properties":{"imageId":{"type":"string","format":"uuid","description":"Image identifier"},"url":{"type":"string","format":"uri","description":"Public URL of the image"},"version":{"type":"integer","description":"Version number"},"persisted":{"type":"boolean","description":"Whether the image is persisted"}},"required":["imageId","url","version","persisted"]},"SubscriptionInput":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid","nullable":true},"subscription_plan":{"type":"string"},"billing_frequency":{"type":"string","enum":["daily","weekly","monthly","quarterly","yearly"]},"billing_amount":{"type":"number"},"next_billing_date":{"type":"string","format":"date-time"},"trial_end_date":{"type":"string","format":"date-time","nullable":true},"start_date":{"type":"string","format":"date-time","nullable":true},"end_date":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true}},"required":["customer_id","subscription_plan","billing_frequency","billing_amount","next_billing_date"]},"TimeEntryInput":{"type":"object","properties":{"staff_id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["CLOCK_IN","CLOCK_OUT","BREAK","LUNCH"]},"timestamp":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true}},"required":["staff_id","type"]},"WorkOrderInput":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string"},"priority":{"type":"string"},"assigned_to":{"type":"string","format":"uuid","nullable":true},"customer_id":{"type":"string","format":"uuid","nullable":true},"category":{"type":"string","nullable":true},"due_date":{"type":"string","format":"date-time","nullable":true},"metadata":{"type":"object","nullable":true}},"required":["title"]},"WaitlistEntryInput":{"type":"object","properties":{"patient_name":{"type":"string"},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"preferred_date":{"type":"string"},"preferred_time":{"type":"string","nullable":true},"professional_id":{"type":"string","format":"uuid","nullable":true},"notes":{"type":"string","nullable":true}},"required":["patient_name","preferred_date"]},"TreatmentNoteInput":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid"},"professional_id":{"type":"string","format":"uuid"},"booking_id":{"type":"string","format":"uuid","nullable":true},"treatment_type":{"type":"string"},"subjective":{"type":"string","nullable":true},"objective":{"type":"string","nullable":true},"assessment":{"type":"string","nullable":true},"plan":{"type":"string","nullable":true},"diagnosis_codes":{"type":"array","items":{"type":"string"},"nullable":true},"procedure_codes":{"type":"array","items":{"type":"string"},"nullable":true},"products_used":{"type":"array","items":{"type":"string"},"nullable":true},"follow_up_needed":{"type":"boolean","nullable":true},"follow_up_date":{"type":"string","format":"date","nullable":true},"notes":{"type":"string","nullable":true}},"required":["customer_id","professional_id"]},"CommissionFlatFeeDetails":{"type":"object","description":"Detail fields for a FLAT_FEE commission schedule","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the flat-fee detail row"},"schedule_id":{"type":"string","format":"uuid","description":"Parent commission schedule ID"},"flat_amount":{"type":"number","format":"double","description":"Fixed dollar amount paid as commission per transaction","example":25},"minimum_ticket_value":{"type":"number","format":"double","description":"Minimum transaction value required for commission to apply","example":50}},"required":["flat_amount","minimum_ticket_value"]},"CommissionPercentageDetails":{"type":"object","description":"Detail fields for a PERCENTAGE commission schedule","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the percentage detail row"},"schedule_id":{"type":"string","format":"uuid","description":"Parent commission schedule ID"},"percentage_rate":{"type":"number","format":"double","description":"Commission rate expressed as a decimal (e.g. 0.10 = 10%)","example":0.1},"calculated_on":{"type":"string","enum":["GROSS","NET","PRODUCT_COST"],"description":"Basis on which the percentage is calculated","example":"GROSS"},"minimum_ticket_value":{"type":"number","format":"double","description":"Minimum transaction value required for commission to apply","example":0},"maximum_commission_cap":{"type":"number","format":"double","description":"Maximum commission amount that can be earned (0 = no cap)","example":500}},"required":["percentage_rate","calculated_on","minimum_ticket_value","maximum_commission_cap"]},"CommissionTier":{"type":"object","description":"A single tier within a TIERED commission schedule","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the tier row"},"schedule_id":{"type":"string","format":"uuid","description":"Parent commission schedule ID"},"tier_label":{"type":"string","description":"Human-readable label for the tier (e.g. \"Bronze\", \"Silver\")","example":"Bronze"},"threshold_from":{"type":"number","format":"double","description":"Lower bound of revenue/transaction threshold for this tier (inclusive)","example":0},"threshold_to":{"type":"number","format":"double","nullable":true,"description":"Upper bound of the threshold (null = no upper limit)","example":1000},"commission_type":{"type":"string","enum":["FLAT","PERCENTAGE"],"description":"Whether the commission is a flat dollar amount or a percentage","example":"PERCENTAGE"},"rate":{"type":"number","format":"double","description":"Commission rate — dollar amount if commission_type is FLAT, decimal fraction if PERCENTAGE","example":0.05},"sort_order":{"type":"integer","description":"Display/evaluation order (0-indexed)","example":0}},"required":["tier_label","threshold_from","commission_type","rate","sort_order"]},"CommissionTieredDetails":{"type":"object","description":"Detail fields for a TIERED commission schedule","properties":{"tiers":{"type":"array","description":"Ordered list of tiers from lowest to highest threshold","items":{"$ref":"#/components/schemas/CommissionTier"}}},"required":["tiers"]},"CommissionSchedule":{"type":"object","description":"A commission schedule record, enriched with the detail object for its schedule_type","properties":{"id":{"type":"string","format":"uuid","description":"Unique commission schedule identifier","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"store_id":{"type":"string","format":"uuid","description":"Owning store identifier","example":"s1t2u3v4-w5x6-7890-yzab-cd1234567890"},"name":{"type":"string","description":"Human-readable name for the schedule","example":"Standard Retail Commission"},"schedule_type":{"type":"string","enum":["FLAT_FEE","PERCENTAGE","TIERED"],"description":"Determines which detail object is present on the response","example":"PERCENTAGE"},"applies_to":{"type":"string","enum":["SALES","TREATMENTS","BOTH"],"description":"Which transaction categories this schedule applies to","example":"BOTH"},"active":{"type":"boolean","description":"Whether the schedule is currently active","example":true},"notes":{"type":"string","description":"Optional internal notes about the schedule","example":""},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"},"flat_fee":{"$ref":"#/components/schemas/CommissionFlatFeeDetails","description":"Present when schedule_type is FLAT_FEE; null otherwise"},"percentage":{"$ref":"#/components/schemas/CommissionPercentageDetails","description":"Present when schedule_type is PERCENTAGE; null otherwise"},"tiered":{"$ref":"#/components/schemas/CommissionTieredDetails","description":"Present when schedule_type is TIERED; null otherwise"}},"required":["id","store_id","name","schedule_type","applies_to","active","notes","created_at","updated_at"]},"CreateCommissionScheduleRequest":{"type":"object","description":"Request body for creating a new commission schedule","properties":{"name":{"type":"string","description":"Human-readable name for the schedule","example":"Retail Flat Fee"},"schedule_type":{"type":"string","enum":["FLAT_FEE","PERCENTAGE","TIERED"],"description":"Type of commission calculation","example":"FLAT_FEE"},"applies_to":{"type":"string","enum":["SALES","TREATMENTS","BOTH"],"description":"Which transaction categories this schedule applies to (default BOTH)","default":"BOTH","example":"BOTH"},"active":{"type":"boolean","description":"Whether the schedule starts active (default true)","default":true,"example":true},"notes":{"type":"string","description":"Optional internal notes","default":"","example":""},"flat_fee":{"type":"object","description":"Required when schedule_type is FLAT_FEE","properties":{"flat_amount":{"type":"number","format":"double","description":"Fixed dollar amount paid as commission","example":25},"minimum_ticket_value":{"type":"number","format":"double","description":"Minimum ticket value to trigger the commission","example":50}}},"percentage":{"type":"object","description":"Required when schedule_type is PERCENTAGE","properties":{"percentage_rate":{"type":"number","format":"double","description":"Commission rate as a decimal fraction (e.g. 0.10 for 10%)","example":0.1},"calculated_on":{"type":"string","enum":["GROSS","NET","PRODUCT_COST"],"description":"Basis for percentage calculation (default GROSS)","default":"GROSS","example":"GROSS"},"minimum_ticket_value":{"type":"number","format":"double","description":"Minimum ticket value to trigger the commission","example":0},"maximum_commission_cap":{"type":"number","format":"double","description":"Maximum commission amount (0 = no cap)","example":0}}},"tiered":{"type":"object","description":"Required when schedule_type is TIERED","properties":{"tiers":{"type":"array","description":"List of tiers in ascending threshold order","items":{"type":"object","properties":{"tier_label":{"type":"string","description":"Human-readable tier name","example":"Bronze"},"threshold_from":{"type":"number","format":"double","description":"Lower bound of the threshold (inclusive)","example":0},"threshold_to":{"type":"number","format":"double","nullable":true,"description":"Upper bound of the threshold (null = no limit)","example":1000},"commission_type":{"type":"string","enum":["FLAT","PERCENTAGE"],"description":"Flat dollar or percentage commission","example":"PERCENTAGE"},"rate":{"type":"number","format":"double","description":"Commission rate","example":0.05}}}}}}},"required":["name","schedule_type"]},"UpdateCommissionScheduleRequest":{"type":"object","description":"Request body for partially updating a commission schedule.\nThe `schedule_type` cannot be changed after creation.\nOnly the detail object matching the existing `schedule_type` will be processed.\n","properties":{"name":{"type":"string","description":"Updated schedule name","example":"Revised Flat Fee Schedule"},"applies_to":{"type":"string","enum":["SALES","TREATMENTS","BOTH"],"description":"Updated transaction category scope","example":"SALES"},"active":{"type":"boolean","description":"Enable or disable the schedule","example":false},"notes":{"type":"string","description":"Updated internal notes","example":"Deactivated for Q3 review"},"flat_fee":{"type":"object","description":"Updated detail for FLAT_FEE schedules","properties":{"flat_amount":{"type":"number","format":"double","example":30},"minimum_ticket_value":{"type":"number","format":"double","example":75}}},"percentage":{"type":"object","description":"Updated detail for PERCENTAGE schedules","properties":{"percentage_rate":{"type":"number","format":"double","example":0.12},"calculated_on":{"type":"string","enum":["GROSS","NET","PRODUCT_COST"],"example":"NET"},"minimum_ticket_value":{"type":"number","format":"double","example":0},"maximum_commission_cap":{"type":"number","format":"double","example":1000}}},"tiered":{"type":"object","description":"Replacement tier list for TIERED schedules (all existing tiers are replaced)","properties":{"tiers":{"type":"array","items":{"type":"object","properties":{"tier_label":{"type":"string","example":"Silver"},"threshold_from":{"type":"number","format":"double","example":1000.01},"threshold_to":{"type":"number","format":"double","nullable":true,"example":null},"commission_type":{"type":"string","enum":["FLAT","PERCENTAGE"],"example":"PERCENTAGE"},"rate":{"type":"number","format":"double","example":0.08}}}}}}}},"OwnerAlertRule":{"type":"object","description":"A threshold-based alert rule that triggers an owner SMS notification.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"name":{"type":"string","example":"Labor % exceeded"},"trigger_type":{"type":"string","enum":["labor_pct_exceeded","food_cost_pct_exceeded","checklist_missed","task_escalated","review_negative","revenue_below_target","inventory_low","custom"],"example":"labor_pct_exceeded"},"threshold_value":{"type":"number","nullable":true,"example":35},"owner_phone":{"type":"string","example":"+15551234567"},"owner_email":{"type":"string","nullable":true,"example":"owner@example.com"},"is_active":{"type":"boolean","example":true},"snooze_until":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"OwnerAlertRuleInput":{"type":"object","required":["name","trigger_type","owner_phone"],"properties":{"name":{"type":"string","example":"Labor % exceeded"},"trigger_type":{"type":"string","enum":["labor_pct_exceeded","food_cost_pct_exceeded","checklist_missed","task_escalated","review_negative","revenue_below_target","inventory_low","custom"]},"threshold_value":{"type":"number","nullable":true,"example":35},"owner_phone":{"type":"string","example":"+15551234567"},"owner_email":{"type":"string","nullable":true},"is_active":{"type":"boolean","default":true},"snooze_until":{"type":"string","format":"date-time","nullable":true}}},"OwnerAlertRulePatch":{"type":"object","properties":{"name":{"type":"string"},"trigger_type":{"type":"string","enum":["labor_pct_exceeded","food_cost_pct_exceeded","checklist_missed","task_escalated","review_negative","revenue_below_target","inventory_low","custom"]},"threshold_value":{"type":"number","nullable":true},"owner_phone":{"type":"string"},"owner_email":{"type":"string","nullable":true},"is_active":{"type":"boolean"},"snooze_until":{"type":"string","format":"date-time","nullable":true}}},"OwnerAlertLogEntry":{"type":"object","description":"A delivery log entry for a fired owner alert.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"rule_id":{"type":"string","format":"uuid","nullable":true},"trigger_type":{"type":"string","example":"labor_pct_exceeded"},"message":{"type":"string","example":"Labor cost is at 38% — above your 35% threshold."},"channel":{"type":"string","enum":["sms","email","both"],"example":"sms"},"delivered_at":{"type":"string","format":"date-time","nullable":true},"owner_reply":{"type":"string","nullable":true},"reply_action":{"type":"string","enum":["ok","stop","snooze"],"nullable":true},"created_at":{"type":"string","format":"date-time"}}},"OwnerAlertFireInput":{"type":"object","required":["trigger_type","message"],"properties":{"rule_id":{"type":"string","format":"uuid","description":"Optional rule to evaluate. Provides phone, snooze check, and active check."},"trigger_type":{"type":"string","enum":["labor_pct_exceeded","food_cost_pct_exceeded","checklist_missed","task_escalated","review_negative","revenue_below_target","inventory_low","custom"]},"message":{"type":"string","example":"Labor cost is at 38% — above your 35% threshold."},"channel":{"type":"string","enum":["sms","email","both"],"default":"sms"},"owner_phone":{"type":"string","description":"Phone to use when rule_id is not provided or rule has no phone."}}},"OwnerAlertReplyInput":{"type":"object","required":["log_id","reply"],"properties":{"log_id":{"type":"string","format":"uuid","description":"The owner_alert_log entry to reply to."},"reply":{"type":"string","enum":["OK","STOP","SNOOZE"],"description":"Owner reply action (case-insensitive)."},"snooze_minutes":{"type":"integer","description":"Minutes to snooze (used when reply=SNOOZE, default 60).","example":60}}},"StaffTask":{"type":"object","description":"A staff task template defining a recurring shift-level duty.","properties":{"id":{"type":"string","format":"uuid","example":"c1a2b3c4-d5e6-7890-abcd-ef1234567890"},"store_id":{"type":"string","format":"uuid"},"title":{"type":"string","example":"Pre-shift side work checklist"},"role":{"type":"string","nullable":true,"example":"server"},"shift":{"type":"string","enum":["am","pm","closing","all"],"example":"am"},"deadline_offset":{"type":"integer","description":"Minutes after shift start by which the task must be acknowledged","example":30},"is_active":{"type":"boolean","example":true},"created_at":{"type":"string","format":"date-time"}},"required":["id","store_id","title","shift","deadline_offset","is_active","created_at"]},"StaffTaskInput":{"type":"object","description":"Fields to create a staff task template.","properties":{"title":{"type":"string","example":"Pre-shift side work checklist"},"role":{"type":"string","nullable":true,"description":"Target staff role; null means all roles","example":"server"},"shift":{"type":"string","enum":["am","pm","closing","all"],"default":"all","example":"am"},"deadline_offset":{"type":"integer","description":"Minutes after shift start","default":30,"example":30},"is_active":{"type":"boolean","default":true}},"required":["title"]},"StaffTaskAssignment":{"type":"object","description":"A runtime assignment of a task template to a staff member for a specific shift.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"task_id":{"type":"string","format":"uuid"},"staff_id":{"type":"string","format":"uuid","nullable":true},"staff_name":{"type":"string","example":"Jordan Lee"},"staff_phone":{"type":"string","example":"+15555550100"},"due_at":{"type":"string","format":"date-time"},"acknowledged_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","enum":["pending","acknowledged","escalated","skipped"],"example":"pending"},"escalation_step":{"type":"integer","example":0},"created_at":{"type":"string","format":"date-time"}},"required":["id","store_id","task_id","staff_name","staff_phone","due_at","status","escalation_step","created_at"]},"StaffTaskAssignmentInput":{"type":"object","description":"Fields to create a staff task assignment (called by the reminder engine).","properties":{"task_id":{"type":"string","format":"uuid"},"staff_id":{"type":"string","format":"uuid","nullable":true},"staff_name":{"type":"string","example":"Jordan Lee"},"staff_phone":{"type":"string","example":"+15555550100"},"due_at":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["pending","acknowledged","escalated","skipped"],"default":"pending"},"escalation_step":{"type":"integer","default":0}},"required":["task_id","staff_name","staff_phone","due_at"]},"StaffTaskAssignmentPatch":{"type":"object","description":"Fields to acknowledge or escalate an assignment.","properties":{"status":{"type":"string","enum":["pending","acknowledged","escalated","skipped"],"example":"acknowledged"},"escalation_step":{"type":"integer","example":1},"acknowledged_at":{"type":"string","format":"date-time","nullable":true}}},"StaffMember":{"type":"object","description":"A staff member record belonging to a store.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"name":{"type":"string","example":"Alex Rivera"},"role":{"type":"string","example":"Server"},"phone":{"type":"string","nullable":true,"example":"+15555550101"},"email":{"type":"string","nullable":true,"example":"alex@example.com"},"hourly_rate":{"type":"number","format":"float","nullable":true,"example":15.5},"is_active":{"type":"boolean","default":true},"created_at":{"type":"string","format":"date-time"}},"required":["id","store_id","name","role","is_active","created_at"]},"StaffMemberCreate":{"type":"object","description":"Payload to create a staff member.","properties":{"name":{"type":"string","example":"Alex Rivera"},"role":{"type":"string","example":"Server"},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"hourly_rate":{"type":"number","format":"float","nullable":true},"is_active":{"type":"boolean","default":true}},"required":["name","role"]},"StaffMemberPatch":{"type":"object","description":"Fields to update on a staff member.","properties":{"name":{"type":"string"},"role":{"type":"string"},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"hourly_rate":{"type":"number","format":"float","nullable":true},"is_active":{"type":"boolean"}}},"StaffShift":{"type":"object","description":"A scheduled shift for a staff member.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"staff_id":{"type":"string","format":"uuid"},"shift_date":{"type":"string","format":"date","example":"2026-06-20"},"start_time":{"type":"string","example":"09:00"},"end_time":{"type":"string","example":"17:00"},"role_override":{"type":"string","nullable":true},"status":{"type":"string","enum":["scheduled","confirmed","no_show","covered","cancelled"],"default":"scheduled"},"notes":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}},"required":["id","store_id","staff_id","shift_date","start_time","end_time","status","created_at"]},"StaffShiftCreate":{"type":"object","description":"Payload to create a scheduled shift.","properties":{"staff_id":{"type":"string","format":"uuid"},"shift_date":{"type":"string","format":"date","example":"2026-06-20"},"start_time":{"type":"string","example":"09:00"},"end_time":{"type":"string","example":"17:00"},"role_override":{"type":"string","nullable":true},"status":{"type":"string","enum":["scheduled","confirmed","no_show","covered","cancelled"],"default":"scheduled"},"notes":{"type":"string","nullable":true}},"required":["staff_id","shift_date","start_time","end_time"]},"StaffShiftPatch":{"type":"object","description":"Fields to update on a scheduled shift.","properties":{"status":{"type":"string","enum":["scheduled","confirmed","no_show","covered","cancelled"]},"start_time":{"type":"string","example":"09:00"},"end_time":{"type":"string","example":"17:00"},"role_override":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}}},"CoverageGap":{"type":"object","description":"A scheduled shift for which no CLOCK_IN time entry was found within 15 minutes of start_time.","allOf":[{"$ref":"#/components/schemas/StaffShift"},{"type":"object","properties":{"gap_reason":{"type":"string","example":"no_clock_in_within_15min"}}}]},"LaborForecast":{"type":"object","description":"Labor cost forecast for a date range with per-staff breakdown and overtime risk flags.","properties":{"period":{"type":"object","properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}}},"total_scheduled_hours":{"type":"number","example":120},"total_estimated_cost":{"type":"number","example":1800},"projected_revenue":{"type":"number","nullable":true,"example":12000},"labor_pct":{"type":"number","nullable":true,"description":"Estimated labor cost as a percentage of projected_revenue.","example":15},"overtime_risk_count":{"type":"integer","example":1},"staff_forecast":{"type":"array","items":{"type":"object","properties":{"staff_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"role":{"type":"string"},"hourly_rate":{"type":"number","nullable":true},"scheduled_hours":{"type":"number"},"estimated_cost":{"type":"number"},"overtime_risk":{"type":"boolean"}}}}}},"OwnerSummaryLocation":{"type":"object","description":"Per-location financial metrics for one reporting period.","properties":{"store_id":{"type":"string","format":"uuid"},"store_name":{"type":"string","example":"North Park"},"revenue":{"type":"number","example":42180},"order_count":{"type":"integer","example":312},"avg_cover":{"type":"number","example":52.4},"labor_hours":{"type":"number","example":284},"labor_cost":{"type":"number","example":5112},"labor_pct":{"type":"number","example":12.1},"food_cost":{"type":"number","example":14763},"food_cost_pct":{"type":"number","example":35},"gross_profit":{"type":"number","example":22305},"gross_profit_pct":{"type":"number","example":52.9}}},"OwnerSummaryTotals":{"type":"object","description":"Aggregated totals across all locations in the response.","properties":{"revenue":{"type":"number"},"labor_cost":{"type":"number"},"labor_pct":{"type":"number"},"food_cost":{"type":"number"},"food_cost_pct":{"type":"number"},"gross_profit":{"type":"number"},"gross_profit_pct":{"type":"number"}}},"OwnerSummary":{"type":"object","description":"Multi-location financial executive summary for a date range.","properties":{"period":{"type":"object","properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}}},"locations":{"type":"array","items":{"$ref":"#/components/schemas/OwnerSummaryLocation"}},"totals":{"$ref":"#/components/schemas/OwnerSummaryTotals"}}},"DailyBriefing":{"type":"object","description":"Pre-formatted owner briefing text for a reporting day.","properties":{"date":{"type":"string","format":"date","example":"2026-06-10"},"briefing_text":{"type":"string","example":"Good morning. Yesterday across 1 location: $4,218.00 revenue, 31 orders."}}},"ShiftChecklist":{"type":"object","description":"A reusable shift checklist template.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"name":{"type":"string","example":"Opening Checklist"},"shift_type":{"type":"string","enum":["opening","pre_service","closing","custom"]},"is_active":{"type":"boolean","example":true},"created_at":{"type":"string","format":"date-time"}},"required":["id","store_id","name","shift_type","is_active","created_at"]},"ShiftChecklistCreate":{"type":"object","description":"Payload to create a shift checklist template with optional items.","required":["name","shift_type"],"properties":{"name":{"type":"string","example":"Opening Checklist"},"shift_type":{"type":"string","enum":["opening","pre_service","closing","custom"]},"is_active":{"type":"boolean","default":true},"items":{"type":"array","items":{"type":"object","required":["title"],"properties":{"title":{"type":"string","example":"Unlock front door"},"requires_photo":{"type":"boolean","default":false},"sort_order":{"type":"integer","default":0}}}}}},"ShiftChecklistItem":{"type":"object","description":"An item within a shift checklist template.","properties":{"id":{"type":"string","format":"uuid"},"checklist_id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"title":{"type":"string","example":"Check walk-in temp"},"requires_photo":{"type":"boolean","example":false},"sort_order":{"type":"integer","example":0}},"required":["id","checklist_id","store_id","title","requires_photo","sort_order"]},"ShiftChecklistRun":{"type":"object","description":"A single execution instance of a checklist template for a shift.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"checklist_id":{"type":"string","format":"uuid"},"shift_date":{"type":"string","format":"date","example":"2026-06-17"},"started_by":{"type":"string","nullable":true,"example":"Maria"},"completed_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","enum":["in_progress","completed","missed"],"example":"in_progress"},"created_at":{"type":"string","format":"date-time"}},"required":["id","store_id","checklist_id","shift_date","status","created_at"]},"ShiftChecklistRunCreate":{"type":"object","description":"Payload to start a new checklist run.","required":["checklist_id"],"properties":{"checklist_id":{"type":"string","format":"uuid"},"shift_date":{"type":"string","format":"date","description":"Defaults to today if omitted."},"started_by":{"type":"string","example":"Maria"}}},"ShiftChecklistCompletion":{"type":"object","description":"Records an individual item check-off within a run.","properties":{"id":{"type":"string","format":"uuid"},"run_id":{"type":"string","format":"uuid"},"item_id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"checked_at":{"type":"string","format":"date-time"},"checked_by":{"type":"string","nullable":true},"photo_url":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}},"required":["id","run_id","item_id","store_id","checked_at"]},"ShiftChecklistCompleteItem":{"type":"object","description":"Payload to check off an item in a run.","required":["item_id"],"properties":{"item_id":{"type":"string","format":"uuid"},"checked_by":{"type":"string","example":"Maria"},"photo_url":{"type":"string","nullable":true,"example":"https://cdn.example.com/photos/walkin.jpg"},"notes":{"type":"string","nullable":true}}},"ExternalReview":{"type":"object","description":"A review ingested from an external platform (Google, Yelp, etc.)","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"platform":{"type":"string","enum":["google","yelp","tripadvisor","custom"],"example":"google"},"external_id":{"type":"string","example":"ChIJreview12345"},"author_name":{"type":"string","nullable":true,"example":"Jane D."},"rating":{"type":"integer","minimum":1,"maximum":5,"example":4},"body":{"type":"string","nullable":true,"example":"Great food, but service was slow."},"sentiment_score":{"type":"number","nullable":true,"minimum":-1,"maximum":1,"example":-0.15},"reply_body":{"type":"string","nullable":true},"replied_at":{"type":"string","format":"date-time","nullable":true},"review_date":{"type":"string","format":"date-time","example":"2026-06-15T14:30:00Z"},"alert_fired":{"type":"boolean","example":false},"created_at":{"type":"string","format":"date-time"}}},"ExternalReviewInput":{"type":"object","description":"Payload for ingesting a single review from an external platform.","required":["platform","external_id","rating","review_date"],"properties":{"platform":{"type":"string","enum":["google","yelp","tripadvisor","custom"],"example":"google"},"external_id":{"type":"string","example":"ChIJreview12345"},"author_name":{"type":"string","nullable":true,"example":"Jane D."},"rating":{"type":"integer","minimum":1,"maximum":5,"example":2},"body":{"type":"string","nullable":true,"example":"Cold food and long wait."},"sentiment_score":{"type":"number","nullable":true,"minimum":-1,"maximum":1},"review_date":{"type":"string","format":"date-time","example":"2026-06-15T14:30:00Z"}}},"ReviewSummary":{"type":"object","description":"Aggregate reputation metrics across all platforms.","properties":{"total_reviews":{"type":"integer"},"avg_rating":{"type":"number","nullable":true,"example":3.87},"pct_negative":{"type":"number","description":"Percentage of reviews with rating <= 2","example":12.5},"platforms":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string","enum":["google","yelp","tripadvisor","custom"]},"total_reviews":{"type":"integer"},"avg_rating":{"type":"number","nullable":true},"negative_count":{"type":"integer"},"pct_negative":{"type":"number"}}}},"period":{"type":"object","properties":{"from":{"type":"string","format":"date-time","nullable":true},"to":{"type":"string","format":"date-time","nullable":true}}}}},"PosConnector":{"type":"object","description":"An external POS connector registration. Credentials are never returned.","properties":{"id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"provider":{"type":"string","enum":["toast","square","clover","nopos","custom"],"description":"External POS provider"},"location_id":{"type":"string","nullable":true,"description":"Provider-specific location identifier"},"is_active":{"type":"boolean","default":true},"last_synced_at":{"type":"string","format":"date-time","nullable":true},"sync_status":{"type":"string","enum":["idle","syncing","error","ok"],"nullable":true},"sync_error":{"type":"string","nullable":true,"description":"Error message from the last failed sync"},"created_at":{"type":"string","format":"date-time"}}},"PosConnectorCreateRequest":{"type":"object","required":["provider"],"properties":{"provider":{"type":"string","enum":["toast","square","clover","nopos","custom"]},"credentials":{"type":"object","additionalProperties":true,"description":"Opaque provider credentials (API keys, tokens). Stored encrypted. Never returned in GET responses."},"location_id":{"type":"string","nullable":true},"is_active":{"type":"boolean","default":true}}},"PosConnectorPatchRequest":{"type":"object","description":"At least one field must be supplied.","properties":{"credentials":{"type":"object","additionalProperties":true,"description":"Replace the stored credentials blob."},"location_id":{"type":"string","nullable":true},"is_active":{"type":"boolean"}}},"PosSyncLogEntry":{"type":"object","description":"A single sync attempt record (append-only).","properties":{"id":{"type":"string","format":"uuid"},"connector_id":{"type":"string","format":"uuid"},"store_id":{"type":"string","format":"uuid"},"synced_at":{"type":"string","format":"date-time"},"orders_imported":{"type":"integer","default":0},"revenue_total":{"type":"number","format":"double","nullable":true},"status":{"type":"string","enum":["ok","partial","error"]},"error_message":{"type":"string","nullable":true}}},"PosSyncTriggerResult":{"type":"object","properties":{"connector_id":{"type":"string","format":"uuid"},"from_date":{"type":"string","format":"date"},"to_date":{"type":"string","format":"date"},"orders_imported":{"type":"integer"},"revenue_total":{"type":"number","nullable":true},"status":{"type":"string","enum":["ok","partial","error"]},"error_message":{"type":"string","nullable":true}}}},"responses":{"RateLimitExceeded":{"description":"Rate limit exceeded. Back off until the reset time before retrying.","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active window","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active window resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying when available","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}}}},"paths":{"/":{"get":{"summary":"API Health Check","description":"Returns basic API information and health status","tags":["Health"],"responses":{"200":{"description":"API is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"NOPOS API is running"},"version":{"type":"string","example":"1.1.0"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/stores":{"get":{"summary":"Get Store Information","description":"Returns information about the store associated with the authenticated API key","tags":["Stores"],"responses":{"200":{"description":"Store information retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Store not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create New Store","description":"Creates a new store with the provided information","tags":["Stores"],"requestBody":{"required":true,"description":"Store creation data","content":{"application/json":{"schema":{"type":"object","required":["name","email"],"properties":{"name":{"type":"string","description":"Store name (required)","example":"My New Store"},"email":{"type":"string","format":"email","description":"Store email (required)","example":"store@example.com"},"description":{"type":"string","description":"Store description"},"address":{"type":"string","description":"Street address"},"city":{"type":"string","description":"City"},"state":{"type":"string","description":"State or province"},"zip_code":{"type":"string","description":"ZIP or postal code"},"country":{"type":"string","description":"Country code","default":"US"},"phone":{"type":"string","description":"Phone number"},"website":{"type":"string","format":"uri","description":"Website URL"},"timezone":{"type":"string","description":"Store timezone","default":"America/New_York"},"business_hours":{"type":"object","description":"Business hours configuration","example":{"monday":{"open":"09:00","close":"17:00"},"tuesday":{"open":"09:00","close":"17:00"}}},"settings":{"type":"object","description":"Store-specific settings","example":{"currency":"USD","tax_rate":0.08}}}}}}},"responses":{"201":{"description":"Store created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"400":{"description":"Bad Request - Invalid or missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missingName":{"summary":"Missing store name","value":{"error":"Store name is required"}},"missingEmail":{"summary":"Missing store email","value":{"error":"Store email is required"}},"invalidEmail":{"summary":"Invalid email format","value":{"error":"Invalid email format"}},"invalidWebsite":{"summary":"Invalid website URL","value":{"error":"Invalid website URL format"}}}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/stores/{id}":{"get":{"summary":"Get Store by ID","description":"Returns store information by ID (must match the store associated with the API key)","tags":["Stores"],"parameters":[{"name":"id","in":"path","required":true,"description":"Store UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Store information retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden - Access denied to different store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Store not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update Store Information","description":"Updates store information (only for the store associated with the API key)","tags":["Stores"],"parameters":[{"name":"id","in":"path","required":true,"description":"Store UUID","schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Store update data","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Store name"},"description":{"type":"string","description":"Store description"},"address":{"type":"string","description":"Street address"},"city":{"type":"string","description":"City"},"state":{"type":"string","description":"State or province"},"zip_code":{"type":"string","description":"ZIP or postal code"},"phone":{"type":"string","description":"Phone number"},"email":{"type":"string","format":"email","description":"Email address"},"website":{"type":"string","format":"uri","description":"Website URL"},"timezone":{"type":"string","description":"Store timezone"},"business_hours":{"type":"object","description":"Business hours configuration"},"settings":{"type":"object","description":"Store-specific settings"}}}}}},"responses":{"200":{"description":"Store updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"400":{"description":"Bad Request - Invalid data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden - Access denied to different store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete Store (Soft Delete)","description":"Soft deletes a store by setting archived = true and is_active = false.\nThis operation will fail if the store has active resources (products, orders, or bookings).\n","tags":["Stores"],"parameters":[{"name":"id","in":"path","required":true,"description":"Store UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Store deleted successfully (soft delete)"},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden - Access denied to different store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Access denied: You can only delete your own store"}}}},"404":{"description":"Store not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict - Store has active resources that must be handled first","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message","example":"Cannot delete store with active products"},"details":{"type":"string","description":"Additional details about the conflict","example":"Store has 5 active product(s). Archive or delete products first."},"active_resources":{"type":"object","description":"Count of active resources preventing deletion","properties":{"products":{"type":"integer","description":"Number of active products"},"orders":{"type":"integer","description":"Number of active orders"},"bookings":{"type":"integer","description":"Number of active bookings"}}}}}}}}}}},"/stores/{id}/archive":{"post":{"summary":"Archive Store","description":"Archives a store (soft delete)","tags":["Stores"],"parameters":[{"name":"id","in":"path","required":true,"description":"Store UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Store archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Store archived successfully"},"store":{"$ref":"#/components/schemas/Store"}}}}}}}}},"/stores/{id}/unarchive":{"post":{"summary":"Unarchive Store","description":"Unarchives a store","tags":["Stores"],"parameters":[{"name":"id","in":"path","required":true,"description":"Store UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Store unarchived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Store unarchived successfully"},"store":{"$ref":"#/components/schemas/Store"}}}}}}}}},"/stores/stats":{"get":{"summary":"Get Store Statistics","description":"Returns basic statistics about the store","tags":["Stores"],"responses":{"200":{"description":"Store statistics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreStats"}}}}}}},"/products":{"get":{"summary":"List Products","description":"Returns a paginated list of products for the authenticated store","tags":["Products"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"},{"name":"archived","in":"query","description":"Filter by archived status","schema":{"type":"boolean"}},{"name":"product_type","in":"query","description":"Filter by product type","schema":{"type":"string","enum":["product","service"]}}],"responses":{"200":{"description":"Products retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/PaginationMetadata"}}},"example":{"data":[{"id":"123e4567-e89b-12d3-a456-426614174000","name":"Test Product","price":29.99,"product_type":"product"}],"pagination":{"total":150,"count":20,"page":1,"pages":8,"limit":20,"offset":0,"hasNext":true,"hasPrev":false}}}}}}},"post":{"summary":"Create Product","description":"Creates a new product","tags":["Products"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Product name"},"sku":{"type":"string","description":"Stock Keeping Unit"},"price":{"type":"number","format":"decimal","description":"Product price"},"description":{"type":"string","description":"Product description"},"product_type":{"type":"string","enum":["product","service"],"default":"product"},"metadata":{"type":"object","description":"Additional product metadata"}},"required":["name","price"]}}}},"responses":{"201":{"description":"Product created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}}}}},"/products/{id}":{"get":{"summary":"Get Product by ID","description":"Returns a specific product by ID","tags":["Products"],"parameters":[{"name":"id","in":"path","required":true,"description":"Product UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Product retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}}}},"patch":{"summary":"Update Product","description":"Updates a product","tags":["Products"],"parameters":[{"name":"id","in":"path","required":true,"description":"Product UUID","schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"sku":{"type":"string"},"price":{"type":"number","format":"decimal"},"description":{"type":"string"},"metadata":{"type":"object"}}}}}},"responses":{"200":{"description":"Product updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}}}},"delete":{"summary":"Delete Product","description":"Deletes a product","tags":["Products"],"parameters":[{"name":"id","in":"path","required":true,"description":"Product UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Product deleted successfully"}}}},"/upload":{"post":{"summary":"Upload Image File","description":"Upload an image file to Supabase Storage. Supports JPEG, PNG, WebP, and GIF images up to 10MB.","tags":["Upload"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"Image file (JPEG, PNG, WebP, or GIF, max 10MB)"}},"required":["image"]}}}},"responses":{"201":{"description":"Image uploaded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Image uploaded successfully"},"url":{"type":"string","format":"uri","description":"Public URL of the uploaded image"},"filename":{"type":"string","description":"Storage path of the uploaded file"},"size":{"type":"integer","description":"File size in bytes"},"mimetype":{"type":"string","description":"MIME type of the uploaded file"}}}}}},"400":{"description":"Bad Request - Invalid file type or size","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/upload/test":{"get":{"summary":"Test Upload Service","description":"Check the status of the upload service and storage bucket availability","tags":["Upload"],"responses":{"200":{"description":"Upload service status","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Upload service status"},"bucketReady":{"type":"boolean","description":"Whether the storage bucket is ready"},"buckets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"public":{"type":"boolean"}}}},"error":{"type":"string","nullable":true}}}}}}}}},"/slides":{"get":{"summary":"List All Slides","description":"Get all slides for the authenticated store with optional filtering","tags":["Slides"],"parameters":[{"name":"status","in":"query","description":"Filter by status","schema":{"type":"string","enum":["draft","published","archived"]}},{"name":"tag","in":"query","description":"Filter by tag","schema":{"type":"string"}},{"name":"order_by","in":"query","description":"Order results by field","schema":{"type":"string","enum":["created_at","order","title"],"default":"created_at"}},{"name":"order_direction","in":"query","description":"Order direction","schema":{"type":"string","enum":["asc","desc"],"default":"asc"}}],"responses":{"200":{"description":"Slides retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Slide"}}}}}}},"post":{"summary":"Create Slide","description":"Create a new slide with text content","tags":["Slides"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideInput"}}}},"responses":{"201":{"description":"Slide created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Slide created successfully"},"data":{"$ref":"#/components/schemas/Slide"}}}}}}}}},"/slides/with-image":{"post":{"summary":"Create Slide with Image Upload","description":"Create a new slide with an uploaded image","tags":["Slides"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"Image file (optional)"},"title":{"type":"string","description":"Slide title (required)"},"body":{"type":"string","description":"Slide content"},"background_color":{"type":"string","description":"Hex color (e.g.,"},"text_color":{"type":"string","description":"Hex color (e.g.,"},"font_size":{"type":"string","description":"CSS size (e.g., 24px, 1.5em)"},"text_position":{"type":"string","enum":["top","center","bottom"]},"animation":{"type":"string","description":"Animation name"},"duration":{"type":"number","description":"Duration in seconds (1-300)"},"order":{"type":"integer","description":"Display order"},"tags":{"type":"string","description":"JSON array or comma-separated tags"},"ai_generated":{"type":"boolean","description":"Whether AI generated"},"prompt_used":{"type":"string","description":"AI prompt used"},"status":{"type":"string","enum":["draft","published","archived"]}},"required":["title"]}}}},"responses":{"201":{"description":"Slide created successfully with image","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"data":{"type":"object","properties":{"slide":{"$ref":"#/components/schemas/Slide"},"image_uploaded":{"type":"boolean"},"image_url":{"type":"string","format":"uri"}}}}}}}}}}},"/slides/stats":{"get":{"summary":"Get Slide Statistics","description":"Get statistics about slides for the authenticated store","tags":["Slides"],"responses":{"200":{"description":"Slide statistics retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"published":{"type":"integer"},"draft":{"type":"integer"},"archived":{"type":"integer"},"with_images":{"type":"integer"},"ai_generated":{"type":"integer"}}}}}}}}},"/slides/bulk-update":{"post":{"summary":"Bulk Update Slides","description":"Update multiple slides at once (useful for reordering)","tags":["Slides"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slides":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"order":{"type":"integer"},"status":{"type":"string","enum":["draft","published","archived"]}},"required":["id"]}}},"required":["slides"]}}}},"responses":{"200":{"description":"Bulk update completed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"updated":{"type":"integer"},"error_count":{"type":"integer"},"results":{"type":"array","items":{"$ref":"#/components/schemas/Slide"}}}}}}}}}},"/slides/{id}":{"get":{"summary":"Get Slide by ID","description":"Get a specific slide by its ID","tags":["Slides"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Slide retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Slide"}}}},"404":{"description":"Slide not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Update Slide","description":"Update an existing slide","tags":["Slides"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideInput"}}}},"responses":{"200":{"description":"Slide updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Slide"}}}}}},"delete":{"summary":"Delete Slide","description":"Delete a slide","tags":["Slides"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Slide deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Slide deleted successfully"}}}}}}}}},"/images/generate-and-store":{"post":{"summary":"Generate and Store Image","description":"Generate an image using AI (OpenAI DALL-E) and store it in Supabase Storage","tags":["Images"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","description":"Description of the image to generate"},"size":{"type":"string","enum":["1024x1024","1792x1024","1024x1792"],"default":"1024x1024","description":"Image size"},"model":{"type":"string","default":"gpt-image-1","description":"AI model to use"},"format":{"type":"string","enum":["png","jpg","webp"],"default":"png","description":"Output format"},"alt":{"type":"string","description":"Alt text for the image"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags for the image"},"usage":{"type":"string","description":"Usage context (e.g., homepage, marketing)"},"metadata":{"type":"object","description":"Additional metadata"}},"required":["prompt"]}}}},"responses":{"200":{"description":"Duplicate image found, metadata updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageAssetResponse"}}}},"201":{"description":"Image generated and stored successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageAssetResponse"}}}},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/images/store":{"post":{"summary":"Store External Image","description":"Persist an externally generated/preview image to Supabase Storage","tags":["Images"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sourceUrl":{"type":"string","format":"uri","description":"URL of the image to store"},"imageId":{"type":"string","format":"uuid","description":"Existing image ID to update"},"alt":{"type":"string","description":"Alt text for the image"},"tags":{"type":"array","items":{"type":"string"}},"usage":{"type":"string"},"metadata":{"type":"object"}}}}}},"responses":{"200":{"description":"Existing image returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageAssetResponse"}}}},"201":{"description":"Image stored successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageAssetResponse"}}}}}}},"/images/{id}":{"get":{"summary":"Get Image Asset","description":"Fetch a stored image asset by ID","tags":["Images"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Image asset retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageAsset"}}}},"404":{"description":"Image not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update Image Asset","description":"Update metadata/fields for an image asset","tags":["Images"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"usage":{"type":"string"},"metadata":{"type":"object"},"status":{"type":"string","enum":["persisted","preview","archived"]},"persisted":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Image asset updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageAsset"}}}}}},"delete":{"summary":"Archive Image Asset","description":"Soft delete an image by setting status=archived and persisted=false","tags":["Images"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Image archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Image archived"},"imageId":{"type":"string","format":"uuid"}}}}}}}}},"/images/usage/analytics":{"get":{"summary":"Get Usage Analytics","description":"Get usage analytics for image generation and storage operations","tags":["Images"],"parameters":[{"name":"timeframe","in":"query","description":"Time period for analytics","schema":{"type":"string","enum":["24h","7d","30d","90d"],"default":"30d"}},{"name":"resource_type","in":"query","description":"Filter by resource type","schema":{"type":"string"}}],"responses":{"200":{"description":"Usage analytics retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"timeframe":{"type":"string"},"total_requests":{"type":"integer"},"total_cost":{"type":"number"},"by_resource_type":{"type":"object"},"by_day":{"type":"object"}}}}}}}}},"/images-async/generate-async":{"post":{"summary":"Start Async Image Generation","description":"Start an asynchronous image generation job (returns immediately with job ID)","tags":["Images Async"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","description":"Description of the image to generate"},"size":{"type":"string","enum":["1024x1024","1792x1024","1024x1792"],"default":"1792x1024"},"quality":{"type":"string","enum":["standard","hd"],"default":"standard"},"style":{"type":"string","enum":["natural","vivid"],"default":"natural"}},"required":["prompt"]}}}},"responses":{"202":{"description":"Image generation started","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"data":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","example":"pending"},"statusUrl":{"type":"string"}}}}}}}}}}},"/images-async/status/{jobId}":{"get":{"summary":"Check Job Status","description":"Check the status of an asynchronous image generation job","tags":["Images Async"],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job status retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"data":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","processing","completed","failed"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true},"result":{"type":"object","properties":{"imageUrl":{"type":"string"},"revisedPrompt":{"type":"string"}}},"error":{"type":"string","nullable":true}}}}}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/health":{"x-internal":true,"x-base-path-note":"Mounted at the root (without /v1 prefix) — access as https://nopos.vercel.app/health, not /v1/health","get":{"summary":"Basic Health Check","description":"Returns basic service health status.\n\n**Note:** This endpoint is mounted at the root path, not under `/v1`.\nThe actual URL is `https://nopos.vercel.app/health` (no version prefix).\n","tags":["Health"],"security":[],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"uptime":{"type":"number"},"message":{"type":"string","example":"OK"},"timestamp":{"type":"integer"},"service":{"type":"string","example":"NOPOS API"},"version":{"type":"string","example":"1.1.0"}}}}}}}}},"/health/detailed":{"x-internal":true,"x-base-path-note":"Mounted at the root (without /v1 prefix) — access as https://nopos.vercel.app/health/detailed","get":{"summary":"Detailed Health Check","description":"Checks database connectivity, memory, and environment.\n\n**Note:** Mounted at root path — actual URL is `https://nopos.vercel.app/health/detailed`.\n","tags":["Health"],"security":[],"responses":{"200":{"description":"Detailed health status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"checks":{"type":"object","properties":{"database":{"type":"object"},"memory":{"type":"object"},"environment":{"type":"object"}}}}}}}}}}},"/health/ready":{"x-internal":true,"x-base-path-note":"Mounted at the root (without /v1 prefix) — access as https://nopos.vercel.app/health/ready","get":{"summary":"Readiness Check","description":"Determines if the service is ready to accept traffic.\n\n**Note:** Mounted at root path — actual URL is `https://nopos.vercel.app/health/ready`.\n","tags":["Health"],"security":[],"responses":{"200":{"description":"Service is ready","content":{"application/json":{"schema":{"type":"object","properties":{"ready":{"type":"boolean"},"message":{"type":"string"}}}}}}}}},"/health/live":{"x-internal":true,"x-base-path-note":"Mounted at the root (without /v1 prefix) — access as https://nopos.vercel.app/health/live","get":{"summary":"Liveness Check","description":"Determines if the service is running.\n\n**Note:** Mounted at root path — actual URL is `https://nopos.vercel.app/health/live`.\n","tags":["Health"],"security":[],"responses":{"200":{"description":"Service is alive","content":{"application/json":{"schema":{"type":"object","properties":{"alive":{"type":"boolean"}}}}}}}}},"/settings":{"get":{"summary":"Get Store Settings","description":"Retrieve store settings deep-merged with defaults","tags":["Settings"],"responses":{"200":{"description":"Settings retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object"}}}}}}}},"patch":{"summary":"Update Store Settings","description":"Merge-update store settings","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Settings updated"}}}},"/subscriptions":{"get":{"summary":"List Subscriptions","tags":["Subscriptions"],"parameters":[{"name":"customer_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"List of subscriptions"}}},"post":{"summary":"Create Subscription","tags":["Subscriptions"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionInput"}}}},"responses":{"201":{"description":"Subscription created"}}}},"/subscriptions/{id}":{"get":{"summary":"Get Subscription","tags":["Subscriptions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Subscription retrieved"}}},"patch":{"summary":"Update Subscription","tags":["Subscriptions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Subscription updated"}}},"delete":{"summary":"Delete Subscription","tags":["Subscriptions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"hard_delete","in":"query","schema":{"type":"boolean","default":false}}],"responses":{"204":{"description":"Subscription deleted"}}}},"/time-entries":{"get":{"summary":"List Time Entries","tags":["Time Entries"],"parameters":[{"name":"staff_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"date","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Time entries retrieved"}}},"post":{"summary":"Create Time Entry","tags":["Time Entries"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryInput"}}}},"responses":{"201":{"description":"Time entry created"}}}},"/time-entries/staff/{staff_id}/today":{"get":{"summary":"Get Today's Time Entries","tags":["Time Entries"],"parameters":[{"name":"staff_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Today's entries retrieved"}}}},"/work-orders":{"get":{"summary":"List Work Orders","tags":["Work Orders"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"},{"name":"status","in":"query","schema":{"type":"string"}},{"name":"priority","in":"query","schema":{"type":"string"}},{"name":"assigned_to","in":"query","schema":{"type":"string"}},{"name":"customer_id","in":"query","schema":{"type":"string"}},{"name":"category","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Work orders retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Work Order","tags":["Work Orders"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrderInput"}}}},"responses":{"201":{"description":"Work order created"}}}},"/work-orders/{id}":{"get":{"summary":"Get Work Order","tags":["Work Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Work order retrieved"}}},"patch":{"summary":"Update Work Order","tags":["Work Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Work order updated"}}},"delete":{"summary":"Delete Work Order","tags":["Work Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Work order deleted"}}}},"/waitlist":{"get":{"summary":"List Waitlist Entries","tags":["Waitlist"],"parameters":[{"name":"preferred_date","in":"query","schema":{"type":"string"}},{"name":"professional_id","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Waitlist entries retrieved"}}},"post":{"summary":"Add Waitlist Entry","tags":["Waitlist"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistEntryInput"}}}},"responses":{"201":{"description":"Waitlist entry created"}}}},"/waitlist/match":{"get":{"summary":"Match Waitlist Entries","tags":["Waitlist"],"parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string"}},{"name":"time","in":"query","schema":{"type":"string"}},{"name":"professional_id","in":"query","schema":{"type":"string"}},{"name":"time_tolerance_minutes","in":"query","schema":{"type":"integer","default":30}}],"responses":{"200":{"description":"Matching entries retrieved"}}}},"/waitlist/{id}":{"delete":{"summary":"Remove Waitlist Entry","tags":["Waitlist"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Waitlist entry removed"}}}},"/treatment-notes":{"get":{"summary":"List Treatment Notes","tags":["Treatment Notes"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"},{"name":"customer_id","in":"query","schema":{"type":"string"}},{"name":"professional_id","in":"query","schema":{"type":"string"}},{"name":"booking_id","in":"query","schema":{"type":"string"}},{"name":"treatment_type","in":"query","schema":{"type":"string"}},{"name":"is_signed","in":"query","schema":{"type":"boolean"}},{"name":"from_date","in":"query","schema":{"type":"string"}},{"name":"to_date","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Treatment notes retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Treatment Note","tags":["Treatment Notes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreatmentNoteInput"}}}},"responses":{"201":{"description":"Treatment note created"}}}},"/treatment-notes/{id}":{"get":{"summary":"Get Treatment Note","tags":["Treatment Notes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Treatment note retrieved"}}},"patch":{"summary":"Update Treatment Note","tags":["Treatment Notes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Treatment note updated"}}},"delete":{"summary":"Delete Treatment Note","tags":["Treatment Notes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Treatment note deleted"}}}},"/stores/all":{"get":{"summary":"List All Stores in Organization","tags":["Stores"],"responses":{"200":{"description":"Stores retrieved"}}}},"/locations":{"get":{"summary":"Get Location Information (alias for /stores)","description":"Canonical alias for GET /stores. Returns information about the location (store) associated with the authenticated API key. Response includes both `id` and `location_id` (equal to `id`) alongside legacy `store_id`-keyed fields.","tags":["Locations"],"responses":{"200":{"description":"Location information retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Location not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create New Location (alias for POST /stores)","description":"Canonical alias for POST /stores. Creates a new location (store).","tags":["Locations"],"requestBody":{"required":true,"description":"Location creation data","content":{"application/json":{"schema":{"$ref":"#/paths/~1stores/post/requestBody/content/application~1json/schema"}}}},"responses":{"201":{"description":"Location created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations/{id}":{"get":{"summary":"Get Location by ID (alias for /stores/{id})","description":"Canonical alias for GET /stores/{id}. Returns location information by ID.","tags":["Locations"],"parameters":[{"name":"id","in":"path","required":true,"description":"Location UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Location information retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden - Access denied to different location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Location not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update Location (alias for PATCH /stores/{id})","description":"Canonical alias for PATCH /stores/{id}. Updates location information.","tags":["Locations"],"parameters":[{"name":"id","in":"path","required":true,"description":"Location UUID","schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Location update data","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip_code":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string","format":"email"},"website":{"type":"string","format":"uri"},"timezone":{"type":"string"}}}}}},"responses":{"200":{"description":"Location updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Store"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations/all":{"get":{"summary":"List All Locations in Organization (alias for /stores/all)","description":"Canonical alias for GET /stores/all. Returns all locations in the same organization.","tags":["Locations"],"responses":{"200":{"description":"Locations retrieved","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Store"}}}}},"401":{"description":"Unauthorized - Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/products/{id}/archive":{"post":{"summary":"Archive Product","tags":["Products"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Product archived"}}}},"/products/{id}/unarchive":{"post":{"summary":"Unarchive Product","tags":["Products"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Product unarchived"}}}},"/platform/auth/login":{"post":{"summary":"Staff Login","tags":["Platform Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Login successful"}}}},"/platform/auth/session":{"get":{"summary":"Get Staff Session","tags":["Platform Auth"],"responses":{"200":{"description":"Session retrieved"}}}},"/platform/auth/refresh":{"post":{"summary":"Refresh Staff Token","tags":["Platform Auth"],"responses":{"200":{"description":"Token refreshed"}}}},"/platform/auth/logout":{"post":{"summary":"Staff Logout","tags":["Platform Auth"],"responses":{"204":{"description":"Logged out"}}}},"/platform/auth/switch-clinic":{"post":{"summary":"Switch Clinic Context","tags":["Platform Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clinic_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Clinic switched"}}}},"/bookings":{"get":{"summary":"List Bookings","tags":["Bookings"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Bookings retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Booking","tags":["Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Booking created"}}}},"/bookings/{id}":{"get":{"summary":"Get Booking","tags":["Bookings"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Booking retrieved"}}},"patch":{"summary":"Update Booking","tags":["Bookings"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Booking updated"}}},"delete":{"summary":"Cancel Booking","tags":["Bookings"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Booking cancelled"}}}},"/customers":{"get":{"summary":"List Customers","tags":["Customers"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Customers retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Customer","tags":["Customers"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Customer created"}}}},"/customers/{id}":{"get":{"summary":"Get Customer","tags":["Customers"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Customer retrieved"}}},"patch":{"summary":"Update Customer","tags":["Customers"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Customer updated"}}},"delete":{"summary":"Delete Customer","tags":["Customers"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Customer deleted"}}}},"/customers/search":{"get":{"summary":"Search Customers","tags":["Customers"],"responses":{"200":{"description":"Search results"}}}},"/professionals":{"get":{"summary":"List Professionals","tags":["Professionals"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Professionals retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Professional","tags":["Professionals"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Professional created"}}}},"/professionals/{id}":{"get":{"summary":"Get Professional","tags":["Professionals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Professional retrieved"}}},"patch":{"summary":"Update Professional","tags":["Professionals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Professional updated"}}},"delete":{"summary":"Delete Professional","tags":["Professionals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Professional deleted"}}}},"/professionals/{id}/availability":{"get":{"summary":"Get Professional Availability","tags":["Professionals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Availability retrieved"}}},"put":{"summary":"Set Professional Availability","tags":["Professionals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Availability updated"}}}},"/professionals/{id}/services":{"post":{"summary":"Link Services to Professional","tags":["Professionals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Services linked"}}}},"/orders":{"get":{"summary":"List Orders","tags":["Orders"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Orders retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Order","tags":["Orders"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Order created"}}}},"/orders/{id}":{"get":{"summary":"Get Order","tags":["Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Order retrieved"}}},"patch":{"summary":"Update Order","tags":["Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Order updated"}}},"delete":{"summary":"Delete Order","tags":["Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Order deleted"}}}},"/payments":{"get":{"summary":"List Payments","tags":["Payments"],"responses":{"200":{"description":"Payments retrieved"}}},"post":{"summary":"Create Payment","description":"Records a payment against an order. NOPOS's first supported mode is external-record-only: the funds are captured by an external processor (Square terminal, Stripe, cash, etc.) and NOPOS records the result. The optional `provider`/`provider_payment_id`/`provider_status` fields distinguish a provider-processed payment from a bare external record. Supplying the `Idempotency-Key` header makes create replay-safe.\n","tags":["Payments"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opt-in idempotency key. A repeated create with the same key returns the already-recorded payment (200) instead of creating a duplicate.\n","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["order_id","payment_method","amount"],"properties":{"order_id":{"type":"string","format":"uuid"},"payment_method":{"type":"string"},"amount":{"type":"number"},"tip":{"type":"number"},"split_index":{"type":"integer"},"split_count":{"type":"integer"},"customer_id":{"type":"string","format":"uuid"},"allocation":{"type":"object","additionalProperties":true},"metadata":{"type":"object","additionalProperties":true},"provider":{"type":"string","description":"Processor that captured the funds (e.g. square, stripe, cash, external)."},"provider_payment_id":{"type":"string","description":"The processor's identifier for the captured payment."},"provider_status":{"type":"string","description":"Provider lifecycle status, when known."}}}}}},"responses":{"200":{"description":"Existing payment returned (idempotent replay)"},"201":{"description":"Payment created"}}}},"/payments/{id}":{"get":{"summary":"Get Payment","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payment retrieved"}}}},"/payments/{id}/refund":{"post":{"summary":"Refund Payment (record-only)","description":"Records a full or partial refund against a payment. External-record-only: NOPOS records that a refund occurred on the processor; it does not itself move money. Omit `amount` for a full refund. Honors `Idempotency-Key`.\n","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"number","description":"Partial refund amount; omit for a full refund."},"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Refund recorded"},"400":{"description":"Invalid refund amount"},"404":{"description":"Payment not found or access denied"}}}},"/content":{"get":{"summary":"List Content","tags":["Content"],"responses":{"200":{"description":"Content retrieved"}}},"post":{"summary":"Create Content","tags":["Content"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Content created"}}}},"/content/{id}":{"get":{"summary":"Get Content","tags":["Content"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Content retrieved"}}},"patch":{"summary":"Update Content","tags":["Content"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Content updated"}}},"delete":{"summary":"Delete Content","tags":["Content"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Content deleted"}}}},"/accounting/accounts":{"get":{"summary":"List Chart of Accounts","description":"Returns the store's chart of accounts ordered by code. ADR-0003 Phase 0.","tags":["Accounting"],"parameters":[{"name":"type","in":"query","required":false,"description":"Filter by account type.","schema":{"type":"string","enum":["asset","liability","equity","revenue","expense"]}},{"name":"is_active","in":"query","required":false,"description":"Filter by active flag.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"}],"responses":{"200":{"description":"Accounts list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["asset","liability","equity","revenue","expense"]},"subtype":{"type":"string","nullable":true},"normal_balance":{"type":"string","enum":["debit","credit"]},"is_system":{"type":"boolean"},"is_active":{"type":"boolean"}}}}}}}}}}},"post":{"summary":"Create Account","description":"Create a custom (non-system) ledger account.","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code","name","type","normal_balance"],"properties":{"code":{"type":"string","example":"4200"},"name":{"type":"string","example":"Catering Revenue"},"type":{"type":"string","enum":["asset","liability","equity","revenue","expense"]},"subtype":{"type":"string","nullable":true},"normal_balance":{"type":"string","enum":["debit","credit"]},"parent_id":{"type":"string","format":"uuid","nullable":true},"currency_code":{"type":"string","example":"USD"},"metadata":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Account created"},"400":{"description":"Validation error"},"409":{"description":"Duplicate account code"}}}},"/accounting/accounts/seed-defaults":{"post":{"summary":"Seed Default Chart of Accounts","description":"Idempotently seed the default chart of accounts (and acct_settings row) for the store. Safe to call repeatedly.","tags":["Accounting"],"responses":{"200":{"description":"Chart of accounts after seeding"}}}},"/accounting/accounts/{account_id}":{"get":{"summary":"Get Account","description":"Fetch a single ledger account by id.","tags":["Accounting"],"parameters":[{"name":"account_id","in":"path","required":true,"description":"Account id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Account"},"404":{"description":"Account not found"}}},"patch":{"summary":"Update Account","description":"Update name/subtype/parent/active/metadata. Code, type and normal_balance are immutable; system accounts cannot be deactivated.","tags":["Accounting"],"parameters":[{"name":"account_id","in":"path","required":true,"description":"Account id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"subtype":{"type":"string","nullable":true},"parent_id":{"type":"string","format":"uuid","nullable":true},"is_active":{"type":"boolean"},"metadata":{"type":"object","additionalProperties":true}}}}}},"responses":{"200":{"description":"Account updated"},"404":{"description":"Account not found"},"409":{"description":"System account cannot be deactivated"}}}},"/accounting/journal-entries":{"get":{"summary":"List Journal Entries","description":"Immutable double-entry journal, newest first. Filter by date range, source_type, or source_event_id.","tags":["Accounting"],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive lower bound on entry_date (YYYY-MM-DD).","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"description":"Inclusive upper bound on entry_date (YYYY-MM-DD).","schema":{"type":"string","format":"date"}},{"name":"source_type","in":"query","required":false,"description":"Filter by provenance type (e.g. manual, payment, refund).","schema":{"type":"string"}},{"name":"source_event_id","in":"query","required":false,"description":"Filter by the idempotency source-event id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"}],"responses":{"200":{"description":"Journal entries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"entry_no":{"type":"integer"},"entry_date":{"type":"string","format":"date"},"description":{"type":"string","nullable":true},"source_type":{"type":"string"},"posting_rule":{"type":"string"},"reverses_entry_id":{"type":"string","format":"uuid","nullable":true},"total_minor":{"type":"integer","description":"Sum of debit legs in minor units (== sum of credit legs)."}}}}}}}}}}},"post":{"summary":"Post Manual Journal Entry","description":"Post a balanced manual journal entry through the acct_post_entry funnel. Debits must equal credits and there must be at least 2 lines, else 400. Pass source_event_id for idempotency (a repeat returns the same entry).","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["entry_date","lines"],"properties":{"entry_date":{"type":"string","format":"date"},"description":{"type":"string"},"currency_code":{"type":"string","example":"USD"},"source_event_id":{"type":"string","format":"uuid","description":"Optional idempotency key."},"posting_rule":{"type":"string","example":"manual@v1"},"lines":{"type":"array","minItems":2,"items":{"type":"object","required":["side","amount_minor"],"properties":{"account_code":{"type":"string","example":"1000"},"account_id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["debit","credit"]},"amount_minor":{"type":"integer","description":"Positive amount in minor units (cents)."},"customer_id":{"type":"string","format":"uuid"},"vendor_id":{"type":"string","format":"uuid"},"professional_id":{"type":"string","format":"uuid"},"memo":{"type":"string"}}}}}},"example":{"entry_date":"2026-06-22","description":"Owner cash injection","lines":[{"account_code":"1000","side":"debit","amount_minor":50000},{"account_code":"3900","side":"credit","amount_minor":50000}]}}}},"responses":{"201":{"description":"Entry posted"},"400":{"description":"Unbalanced or invalid entry, or closed period"},"403":{"description":"Cross-store post rejected"}}}},"/accounting/journal-entries/{entry_id}":{"get":{"summary":"Get Journal Entry","description":"One entry with its lines and a derived is_reversed flag.","tags":["Accounting"],"parameters":[{"name":"entry_id","in":"path","required":true,"description":"Journal entry id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Journal entry with lines"},"404":{"description":"Journal entry not found"}}}},"/accounting/journal-entries/{entry_id}/reverse":{"post":{"summary":"Reverse Journal Entry","description":"Post a reversing entry (each leg's side flipped) pointing back at the original via reverses_entry_id. Corrections are reversals, never edits. Requires reason and approver; a reversal cannot itself be reversed.","tags":["Accounting"],"parameters":[{"name":"entry_id","in":"path","required":true,"description":"Id of the entry to reverse.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason","approver"],"properties":{"reason":{"type":"string"},"approver":{"type":"string"}}}}}},"responses":{"201":{"description":"Reversing entry posted"},"400":{"description":"Cannot reverse (e.g. entry is already a reversal)"},"404":{"description":"Journal entry not found"}}}},"/accounting/vendors":{"get":{"summary":"List AP Vendors","tags":["Accounting"],"responses":{"200":{"description":"Vendors"}}},"post":{"summary":"Create AP Vendor","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"terms_days":{"type":"integer"}}}}}},"responses":{"201":{"description":"Vendor created"}}}},"/accounting/vendors/{vendor_id}":{"patch":{"summary":"Update AP Vendor","tags":["Accounting"],"parameters":[{"name":"vendor_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"terms_days":{"type":"integer"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Vendor updated"},"404":{"description":"Not found"}}}},"/accounting/bills":{"get":{"summary":"List Bills","tags":["Accounting"],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","posted","partial","paid","void"]}},{"name":"vendor_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Bills"}}},"post":{"summary":"Create Bill (draft)","description":"Create a draft vendor bill with expense line items. Post it separately to hit the ledger.","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["vendor_id","issue_date","lines"],"properties":{"vendor_id":{"type":"string","format":"uuid"},"bill_no":{"type":"string"},"issue_date":{"type":"string","format":"date"},"due_date":{"type":"string","format":"date"},"currency_code":{"type":"string"},"memo":{"type":"string"},"lines":{"type":"array","minItems":1,"items":{"type":"object","required":["expense_account_id","unit_price_minor"],"properties":{"description":{"type":"string"},"quantity":{"type":"number"},"unit_price_minor":{"type":"integer"},"amount_minor":{"type":"integer","description":"Defaults to quantity * unit_price_minor."},"expense_account_id":{"type":"string","format":"uuid"}}}}}}}}},"responses":{"201":{"description":"Draft bill created"},"400":{"description":"Validation error"}}}},"/accounting/bills/{bill_id}":{"get":{"summary":"Get Bill","description":"Bill with lines and applied payments.","tags":["Accounting"],"parameters":[{"name":"bill_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Bill"},"404":{"description":"Not found"}}}},"/accounting/bills/{bill_id}/post":{"post":{"summary":"Post Bill","description":"Post a draft bill to the GL (DR expense / CR Accounts Payable).","tags":["Accounting"],"parameters":[{"name":"bill_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Posted; returns entry_id"},"400":{"description":"Not draft or empty"}}}},"/accounting/bills/{bill_id}/payments":{"post":{"summary":"Pay Bill","description":"Pay down a posted bill (DR Accounts Payable / CR cash); updates status to partial or paid.","tags":["Accounting"],"parameters":[{"name":"bill_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_minor"],"properties":{"amount_minor":{"type":"integer"},"source_account_code":{"type":"string","description":"Default 1000 Cash / Bank."},"paid_date":{"type":"string","format":"date"}}}}}},"responses":{"201":{"description":"Payment posted; returns entry_id"},"400":{"description":"Exceeds open balance or invalid"}}}},"/accounting/bills/{bill_id}/void":{"post":{"summary":"Void Bill","description":"Void a bill; reverses its posted entry. Not allowed once payments exist.","tags":["Accounting"],"parameters":[{"name":"bill_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason","approver"],"properties":{"reason":{"type":"string"},"approver":{"type":"string"}}}}}},"responses":{"200":{"description":"Voided"},"400":{"description":"Already void or has payments"}}}},"/accounting/reports/ap-aging":{"get":{"summary":"AP Aging","description":"Open payables bucketed by age (current / 1-30 / 31-60 / 61-90 / 90+), grouped by vendor.","tags":["Accounting"],"parameters":[{"name":"as_of","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"AP aging"}}}},"/accounting/customers":{"get":{"summary":"List AR Customers","tags":["Accounting"],"responses":{"200":{"description":"Customers"}}},"post":{"summary":"Create AR Customer","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"customer_id":{"type":"string","format":"uuid","description":"Soft-link to a NOPOS customer."},"terms_days":{"type":"integer"}}}}}},"responses":{"201":{"description":"Customer created"}}}},"/accounting/customers/{customer_id}":{"patch":{"summary":"Update AR Customer","tags":["Accounting"],"parameters":[{"name":"customer_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"terms_days":{"type":"integer"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Customer updated"},"404":{"description":"Not found"}}}},"/accounting/tax-codes":{"get":{"summary":"List Tax Codes","tags":["Accounting"],"responses":{"200":{"description":"Tax codes"}}},"post":{"summary":"Create Tax Code","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code","name","rate"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"rate":{"type":"number","description":"Fraction 0..1, e.g. 0.0825 for 8.25%."},"tax_account_id":{"type":"string","format":"uuid"},"jurisdiction":{"type":"string"}}}}}},"responses":{"201":{"description":"Tax code created"},"409":{"description":"Duplicate code"}}}},"/accounting/tax-codes/{tax_code_id}":{"patch":{"summary":"Update Tax Code","tags":["Accounting"],"parameters":[{"name":"tax_code_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"rate":{"type":"number"},"tax_account_id":{"type":"string","format":"uuid"},"jurisdiction":{"type":"string"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Tax code updated"},"404":{"description":"Not found"}}}},"/accounting/invoices":{"get":{"summary":"List Invoices","tags":["Accounting"],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","posted","partial","paid","void"]}},{"name":"customer_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Invoices"}}},"post":{"summary":"Create Invoice (draft)","description":"Create a draft invoice with line items. Post it separately to hit the ledger.","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["customer_id","issue_date","lines"],"properties":{"customer_id":{"type":"string","format":"uuid"},"invoice_no":{"type":"string"},"issue_date":{"type":"string","format":"date"},"due_date":{"type":"string","format":"date"},"currency_code":{"type":"string"},"memo":{"type":"string"},"lines":{"type":"array","minItems":1,"items":{"type":"object","required":["revenue_account_id","unit_price_minor"],"properties":{"description":{"type":"string"},"quantity":{"type":"number"},"unit_price_minor":{"type":"integer"},"amount_minor":{"type":"integer","description":"Defaults to quantity * unit_price_minor."},"revenue_account_id":{"type":"string","format":"uuid"},"tax_code_id":{"type":"string","format":"uuid"}}}}}}}}},"responses":{"201":{"description":"Draft invoice created"},"400":{"description":"Validation error"}}}},"/accounting/invoices/{invoice_id}":{"get":{"summary":"Get Invoice","description":"Invoice with lines and applied payments.","tags":["Accounting"],"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Invoice"},"404":{"description":"Not found"}}}},"/accounting/invoices/{invoice_id}/post":{"post":{"summary":"Post Invoice","description":"Post a draft invoice to the GL (DR AR / CR revenue / CR tax).","tags":["Accounting"],"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Posted; returns entry_id"},"400":{"description":"Not draft or empty"}}}},"/accounting/invoices/{invoice_id}/payments":{"post":{"summary":"Apply Payment to Invoice","description":"Record a payment against a posted invoice (DR cash / CR AR); updates status to partial or paid.","tags":["Accounting"],"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_minor"],"properties":{"amount_minor":{"type":"integer"},"deposit_account_code":{"type":"string","description":"Default 1010 Undeposited Funds."},"applied_date":{"type":"string","format":"date"},"payment_ref":{"type":"string"}}}}}},"responses":{"201":{"description":"Payment applied; returns entry_id"},"400":{"description":"Exceeds open balance or invalid"}}}},"/accounting/invoices/{invoice_id}/void":{"post":{"summary":"Void Invoice","description":"Void an invoice; reverses its posted entry. Not allowed once payments are applied.","tags":["Accounting"],"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason","approver"],"properties":{"reason":{"type":"string"},"approver":{"type":"string"}}}}}},"responses":{"200":{"description":"Voided"},"400":{"description":"Already void or has payments"}}}},"/accounting/reports/ar-aging":{"get":{"summary":"AR Aging","description":"Open receivables bucketed by age (current / 1-30 / 31-60 / 61-90 / 90+), grouped by customer.","tags":["Accounting"],"parameters":[{"name":"as_of","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"AR aging"}}}},"/accounting/reports/cash-flow":{"get":{"summary":"Cash Flow","description":"Net change in cash over a period with beginning and ending balances.","tags":["Accounting"],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"method","in":"query","required":false,"schema":{"type":"string","enum":["direct","indirect"]}}],"responses":{"200":{"description":"Cash flow","content":{"application/json":{"schema":{"type":"object","properties":{"beginning_cash_minor":{"type":"integer"},"ending_cash_minor":{"type":"integer"},"net_change_minor":{"type":"integer"}}}}}}}}},"/accounting/reports/trial-balance":{"get":{"summary":"Trial Balance","description":"Per-account debit/credit/balance as of a date. Total debits equal total credits. ADR-0003 Phase 2.","tags":["Accounting"],"parameters":[{"name":"as_of","in":"query","required":false,"description":"As-of date (default today).","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Trial balance","content":{"application/json":{"schema":{"type":"object","properties":{"as_of":{"type":"string","format":"date"},"accounts":{"type":"array","items":{"type":"object"}},"totals":{"type":"object","properties":{"debit_minor":{"type":"integer"},"credit_minor":{"type":"integer"},"balanced":{"type":"boolean"}}}}}}}}}}},"/accounting/reports/profit-and-loss":{"get":{"summary":"Profit and Loss","description":"Revenue and expense activity over a period with net income. Supports cash or accrual basis.","tags":["Accounting"],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start date.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"description":"Inclusive end date (default today).","schema":{"type":"string","format":"date"}},{"name":"basis","in":"query","required":false,"description":"Reporting basis.","schema":{"type":"string","enum":["accrual","cash"]}}],"responses":{"200":{"description":"Profit and loss","content":{"application/json":{"schema":{"type":"object","properties":{"totals":{"type":"object","properties":{"revenue_minor":{"type":"integer"},"expense_minor":{"type":"integer"},"net_income_minor":{"type":"integer"}}}}}}}}}}},"/accounting/reports/balance-sheet":{"get":{"summary":"Balance Sheet","description":"Assets, liabilities and equity as of a date. Assets equal liabilities plus equity.","tags":["Accounting"],"parameters":[{"name":"as_of","in":"query","required":false,"description":"As-of date (default today).","schema":{"type":"string","format":"date"}},{"name":"basis","in":"query","required":false,"description":"Reporting basis.","schema":{"type":"string","enum":["accrual","cash"]}}],"responses":{"200":{"description":"Balance sheet","content":{"application/json":{"schema":{"type":"object","properties":{"totals":{"type":"object","properties":{"assets_minor":{"type":"integer"},"liabilities_minor":{"type":"integer"},"equity_minor":{"type":"integer"},"balanced":{"type":"boolean"}}}}}}}}}}},"/accounting/reports/sales-tax-liability":{"get":{"summary":"Sales Tax Liability","description":"Net Sales Tax Payable accrued over a period.","tags":["Accounting"],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Sales tax liability","content":{"application/json":{"schema":{"type":"object","properties":{"sales_tax_payable_minor":{"type":"integer"}}}}}}}}},"/accounting/reports/general-ledger":{"get":{"summary":"General Ledger for an Account","description":"Journal lines posted to one account with a running balance.","tags":["Accounting"],"parameters":[{"name":"account_id","in":"query","required":true,"description":"Account id.","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/LimitParam"}],"responses":{"200":{"description":"Account ledger"},"400":{"description":"Missing account_id"}}}},"/accounting/periods":{"get":{"summary":"List Accounting Periods","tags":["Accounting"],"responses":{"200":{"description":"Periods"}}},"post":{"summary":"Create Accounting Period","description":"Open a new accounting period over a date range.","tags":["Accounting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["period_start","period_end"],"properties":{"period_start":{"type":"string","format":"date"},"period_end":{"type":"string","format":"date"}}}}}},"responses":{"201":{"description":"Period created"},"409":{"description":"Period already exists"}}}},"/accounting/periods/{period_id}/close":{"post":{"summary":"Close Accounting Period","description":"Close a period (blocks back-dated posts). Refused while POS events for the period are still pending in the outbox.","tags":["Accounting"],"parameters":[{"name":"period_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"closed_by":{"type":"string"}}}}}},"responses":{"200":{"description":"Period closed"},"404":{"description":"Period not found"},"409":{"description":"Already closed or pending outbox events"}}}},"/accounting/periods/{period_id}/reopen":{"post":{"summary":"Reopen Accounting Period","tags":["Accounting"],"parameters":[{"name":"period_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Period reopened"},"404":{"description":"Period not found"},"409":{"description":"Locked periods cannot be reopened"}}}},"/accounting/outbox":{"get":{"summary":"List POS Posting Outbox","description":"Pending and processed POS money events (payment.captured / payment.refunded) emitted by the payments trigger. Filter with `processed=false|true`. ADR-0003 Phase 1.","tags":["Accounting"],"parameters":[{"name":"processed","in":"query","required":false,"description":"Filter by processed state.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/LimitParam"}],"responses":{"200":{"description":"Outbox events","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type":{"type":"string","enum":["payment.captured","payment.refunded"]},"occurred_at":{"type":"string","format":"date-time"},"processed_at":{"type":"string","format":"date-time","nullable":true},"attempts":{"type":"integer"},"error":{"type":"string","nullable":true}}}}}}}}}}}},"/accounting/outbox/process":{"post":{"summary":"Process POS Posting Outbox","description":"Drain pending outbox events into balanced journal entries (sales and refund deltas) for the caller's store. Idempotent. Intended for a cron schedule.","tags":["Accounting"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Max events to drain (default 200, cap 1000)."}}}}}},"responses":{"200":{"description":"Number of events processed","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer"}}}}}}}}},"/accounting/backfill":{"post":{"summary":"Backfill POS Postings","description":"Post historical successful payments for the caller's store as sale entries. Idempotent (one sale per payment). Optionally bounded by a date range.","tags":["Accounting"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}}}}}},"responses":{"200":{"description":"Number of payments posted","content":{"application/json":{"schema":{"type":"object","properties":{"posted":{"type":"integer"}}}}}}}}},"/metrics/daily/{namespace}/{date}":{"put":{"summary":"Upsert Daily Metrics","description":"Idempotent upsert of one day's aggregate metrics for a store, keyed on (store, namespace, date). Re-PUTting the same key overwrites `metrics` and bumps `updated_at`. Returns 201 on first insert, 200 on amend. `date` is the store-local business reporting day (no timezone math). `metrics` is an opaque flat object; values may be mixed types (numbers stay numbers, e.g. `entry_types_seen` is a string).","tags":["Metrics"],"parameters":[{"name":"namespace","in":"path","required":true,"description":"Lowercase snake_case grouping, e.g. `square_recon`.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_]*$"}},{"name":"date","in":"path","required":true,"description":"Business reporting day, store-local.","schema":{"type":"string","format":"date"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["metrics"],"properties":{"metrics":{"type":"object","additionalProperties":true,"description":"Opaque flat object of metric_name -> value."},"source":{"type":"string","nullable":true,"example":"n8n-square-sync"}}},"example":{"source":"n8n-square-sync","metrics":{"gross_sales":822.78,"net_sales":730.03,"taxes":63.53,"tips":131.93,"order_count":51,"square_fees":-25.91,"bank_deposits_pending":899.58,"entry_types_seen":"{\"CHARGE\":89958}"}}}}},"responses":{"200":{"description":"Existing day amended"},"201":{"description":"New day created"}}}},"/metrics/daily/{namespace}/bulk":{"post":{"summary":"Bulk Upsert Daily Metrics","description":"Batch upsert with the same (store, namespace, date) idempotency as the single PUT. Intended for backfill and disaster recovery; accepts large payloads (hundreds of records). Duplicate dates within one payload are deduped last-write-wins.","tags":["Metrics"],"parameters":[{"name":"namespace","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_]*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["records"],"properties":{"records":{"type":"array","items":{"type":"object","required":["date","metrics"],"properties":{"date":{"type":"string","format":"date"},"metrics":{"type":"object","additionalProperties":true},"source":{"type":"string","nullable":true}}}}}}}}},"responses":{"200":{"description":"Records upserted","content":{"application/json":{"schema":{"type":"object","properties":{"namespace":{"type":"string"},"received":{"type":"integer"},"upserted":{"type":"integer"}}}}}}}}},"/metrics/daily/{namespace}":{"get":{"summary":"Read Daily Metrics Range","description":"Returns records ordered by `date` ascending with inclusive `from`/`to` bounds. Sized for a full year in one call (no 20-row pagination). Pass `latest=true` to get just the newest record as an object instead.","tags":["Metrics"],"parameters":[{"name":"namespace","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_]*$"}},{"name":"from","in":"query","required":false,"description":"Inclusive lower bound (YYYY-MM-DD).","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"description":"Inclusive upper bound (YYYY-MM-DD).","schema":{"type":"string","format":"date"}},{"name":"latest","in":"query","required":false,"description":"When `true`, return only the newest record (as an object).","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"Max rows (default and cap 1000).","schema":{"type":"integer"}}],"responses":{"200":{"description":"Metrics retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"metrics":{"type":"object","additionalProperties":true},"updated_at":{"type":"string","format":"date-time"}}}}}}}}}}}},"/metrics/daily/{namespace}/latest":{"get":{"summary":"Latest Daily Metric","description":"Newest record for a namespace; convenient for \"last synced\" checks.","tags":["Metrics"],"parameters":[{"name":"namespace","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_]*$"}}],"responses":{"200":{"description":"Latest record"},"404":{"description":"No records for this namespace"}}}},"/metrics/namespaces":{"get":{"summary":"Namespace Discovery","description":"Returns a summary of all metric namespaces stored for the caller's store. Gives agents and reporting tools a foothold — enumerate what reporting data exists before range-reading it. Returns an empty array (200) when no metrics rows exist.","tags":["Metrics"],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Namespace summary list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"namespace":{"type":"string","example":"square_recon"},"days":{"type":"integer","description":"Number of distinct reporting days stored","example":159},"first_date":{"type":"string","format":"date","example":"2026-01-04"},"last_date":{"type":"string","format":"date","example":"2026-06-10"},"last_updated_at":{"type":"string","format":"date-time","example":"2026-06-11T07:02:11Z"},"sources":{"type":"array","items":{"type":"string"},"example":["n8n-square-sync"]}}}}}}}}},"401":{"description":"Missing or invalid API key"}}}},"/inventory":{"get":{"summary":"List Inventory","tags":["Inventory"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Inventory retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}}},"/inventory/{id}":{"patch":{"summary":"Update inventory item (quantity and/or par-level fields)","tags":["Inventory"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Inventory item UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InventoryPatchRequest"}}}},"responses":{"200":{"description":"Inventory item updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InventoryItem"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Inventory item not found"}}}},"/inventory/below-par":{"get":{"summary":"List items below par level","description":"Returns all inventory items where quantity_on_hand < par_level. Primary polling endpoint for the Hermes depletion alert agent.","tags":["Inventory"],"responses":{"200":{"description":"Below-par items retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InventoryItem"}}}}}}},"401":{"description":"Missing or invalid API key"}}}},"/inventory/waste":{"post":{"summary":"Log a waste event","tags":["Inventory"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WasteLogCreateRequest"}}}},"responses":{"201":{"description":"Waste event logged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WasteLogEntry"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Inventory item not found"}}},"get":{"summary":"List waste log entries","tags":["Inventory"],"parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"Filter from date (YYYY-MM-DD, inclusive)"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Filter to date (YYYY-MM-DD, inclusive)"},{"name":"inventory_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter by inventory item UUID"}],"responses":{"200":{"description":"Waste log entries retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WasteLogEntry"}}}}}}},"400":{"description":"Validation error"},"401":{"description":"Missing or invalid API key"}}}},"/inventory/waste/summary":{"get":{"summary":"Daily waste cost summary grouped by reason","tags":["Inventory"],"parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"Filter from date (YYYY-MM-DD, inclusive)"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Filter to date (YYYY-MM-DD, inclusive)"}],"responses":{"200":{"description":"Waste summary retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WasteSummaryRow"}}}}}}},"400":{"description":"Validation error"},"401":{"description":"Missing or invalid API key"}}}},"/customer-auth/register":{"post":{"summary":"Customer Registration","tags":["Customer Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Customer registered"}}}},"/customer-auth/login":{"post":{"summary":"Customer Login","tags":["Customer Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Login successful"}}}},"/customer-auth/logout":{"post":{"summary":"Customer Logout","tags":["Customer Auth"],"responses":{"200":{"description":"Logout successful"}}}},"/customer-auth/me":{"get":{"summary":"Get Customer Profile","tags":["Customer Auth"],"responses":{"200":{"description":"Profile retrieved"}}},"patch":{"summary":"Update Customer Profile","tags":["Customer Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Profile updated"}}}},"/users/profile":{"get":{"summary":"Get User Profile","tags":["Users"],"responses":{"200":{"description":"Profile retrieved"}}},"put":{"summary":"Update User Profile","tags":["Users"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Profile updated"}}}},"/users/change-password":{"put":{"summary":"Change Password","tags":["Users"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Password changed"}}}},"/ai/generate-content":{"post":{"summary":"Generate AI Content","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIContentGenerationRequest"}}}},"responses":{"200":{"description":"Content generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIContentGenerationResponse"}}}},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/ai/generate-slide":{"post":{"summary":"Generate AI Slide","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AISlideGenerationRequest"}}}},"responses":{"200":{"description":"Slide generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AISlideGenerationResponse"}}}},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/ai/generate-image":{"post":{"summary":"Generate AI Image","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIImageGenerationRequest"}}}},"responses":{"200":{"description":"Image generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIImageGenerationResponse"}}}},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/ai/models":{"get":{"summary":"List AI Models","tags":["AI"],"responses":{"200":{"description":"Models retrieved"}}}},"/ai/status":{"get":{"summary":"AI Service Status","tags":["AI"],"responses":{"200":{"description":"Status retrieved"}}}},"/calendar-blocks":{"get":{"summary":"List Calendar Blocks","tags":["Calendar"],"responses":{"200":{"description":"Blocks retrieved"}}},"post":{"summary":"Create Calendar Block","tags":["Calendar"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Block created"}}}},"/calendar-blocks/{id}":{"patch":{"summary":"Update Calendar Block","tags":["Calendar"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Block updated"}}},"delete":{"summary":"Delete Calendar Block","tags":["Calendar"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Block deleted"}}}},"/calendar-blocks/by-booking/{bookingId}":{"get":{"summary":"Get Blocks by Booking","tags":["Calendar"],"parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Blocks retrieved"}}}},"/capacity/resources":{"get":{"summary":"List Capacity Resources","tags":["Capacity"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Resources retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Capacity Resource","tags":["Capacity"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Resource created"}}}},"/capacity/resources/{id}":{"get":{"summary":"Get Capacity Resource","tags":["Capacity"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Resource retrieved"}}},"patch":{"summary":"Update Capacity Resource","tags":["Capacity"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Resource updated"}}},"delete":{"summary":"Delete Capacity Resource","tags":["Capacity"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Resource deleted"}}}},"/capacity/check":{"post":{"summary":"Check Capacity","tags":["Capacity"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Capacity checked"}}}},"/categories":{"get":{"summary":"List Categories","tags":["Categories"],"responses":{"200":{"description":"Categories retrieved"}}},"post":{"summary":"Create Category","tags":["Categories"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Category created"}}}},"/categories/{id}":{"get":{"summary":"Get Category","tags":["Categories"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Category retrieved"}}},"patch":{"summary":"Update Category","tags":["Categories"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Category updated"}}},"delete":{"summary":"Delete Category","tags":["Categories"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Category deleted"}}}},"/classes":{"get":{"summary":"List Classes","tags":["Classes"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Classes retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Class","tags":["Classes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Class created"}}}},"/classes/{id}":{"get":{"summary":"Get Class","tags":["Classes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Class retrieved"}}},"patch":{"summary":"Update Class","tags":["Classes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Class updated"}}},"delete":{"summary":"Delete Class","tags":["Classes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Class deleted"}}}},"/classes/{id}/participants":{"get":{"summary":"List Class Participants","tags":["Classes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Participants retrieved"}}}},"/classes/{id}/book":{"post":{"summary":"Book Into Class","tags":["Classes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Booking created"}}}},"/estimates":{"get":{"summary":"List Estimates","tags":["Estimates"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Estimates retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Estimate","tags":["Estimates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Estimate created"}}}},"/estimates/{id}":{"get":{"summary":"Get Estimate","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Estimate retrieved"}}},"patch":{"summary":"Update Estimate","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Estimate updated"}}},"delete":{"summary":"Delete Estimate","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Estimate deleted"}}}},"/estimates/{id}/submit":{"post":{"summary":"Submit Estimate","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Estimate submitted"}}}},"/estimates/{id}/approve":{"post":{"summary":"Approve Estimate","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Estimate approved"}}}},"/estimates/{id}/reject":{"post":{"summary":"Reject Estimate","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Estimate rejected"}}}},"/estimates/{id}/convert":{"post":{"summary":"Convert Estimate to Order","tags":["Estimates"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Estimate converted"}}}},"/giftcards":{"get":{"summary":"List Gift Cards","tags":["Gift Cards"],"responses":{"200":{"description":"Gift cards retrieved"}}},"post":{"summary":"Issue Gift Card","tags":["Gift Cards"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Gift card issued"}}}},"/giftcards/{code}":{"get":{"summary":"Get Gift Card","tags":["Gift Cards"],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Gift card retrieved"}}}},"/giftcards/{code}/redeem":{"post":{"summary":"Redeem Gift Card","tags":["Gift Cards"],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Gift card redeemed"}}}},"/giftcards/{code}/void":{"post":{"summary":"Void Gift Card","tags":["Gift Cards"],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Gift card voided"}}}},"/loyalty/members":{"get":{"summary":"List Loyalty Members","tags":["Loyalty"],"responses":{"200":{"description":"Members retrieved"}}}},"/loyalty/enroll":{"post":{"summary":"Enroll in Loyalty","tags":["Loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Customer enrolled"}}}},"/loyalty/award":{"post":{"summary":"Award Points","tags":["Loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Points awarded"}}}},"/loyalty/redeem":{"post":{"summary":"Redeem Reward","tags":["Loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Reward redeemed"}}}},"/loyalty/redemptions":{"get":{"summary":"List Redemptions","tags":["Loyalty"],"responses":{"200":{"description":"Redemptions retrieved"}}}},"/packages":{"get":{"summary":"List Packages","tags":["Packages"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Packages retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Package","tags":["Packages"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Package created"}}}},"/packages/{id}":{"get":{"summary":"Get Package","tags":["Packages"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Package retrieved"}}},"patch":{"summary":"Update Package","tags":["Packages"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Package updated"}}},"delete":{"summary":"Delete Package","tags":["Packages"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Package deleted"}}}},"/patient-vitals":{"get":{"summary":"List Patient Vitals","tags":["Patient Vitals"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Vitals retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Vitals Record","tags":["Patient Vitals"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Vitals created"}}}},"/patient-vitals/{id}":{"get":{"summary":"Get Vitals Record","tags":["Patient Vitals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Vitals retrieved"}}},"patch":{"summary":"Update Vitals Record","tags":["Patient Vitals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Vitals updated"}}},"delete":{"summary":"Delete Vitals Record","tags":["Patient Vitals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Vitals deleted"}}}},"/benefits":{"get":{"summary":"List Benefits","tags":["Benefits"],"responses":{"200":{"description":"Benefits retrieved"}}},"post":{"summary":"Create Benefit","tags":["Benefits"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Benefit created"}}}},"/benefits/{id}":{"patch":{"summary":"Update Benefit","tags":["Benefits"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Benefit updated"}}},"delete":{"summary":"Delete Benefit","tags":["Benefits"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Benefit deleted"}}}},"/benefits/{id}/assign":{"post":{"summary":"Assign Benefit","tags":["Benefits"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Benefit assigned"}}}},"/benefits/patient/{patient_id}":{"get":{"summary":"Get Patient Benefits","tags":["Benefits"],"parameters":[{"name":"patient_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Benefits retrieved"}}}},"/calls":{"get":{"summary":"List Calls","tags":["Calls"],"responses":{"200":{"description":"Calls retrieved"}}},"post":{"summary":"Create Call","tags":["Calls"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Call created"}}}},"/calls/{id}":{"get":{"summary":"Get Call","tags":["Calls"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Call retrieved"}}},"patch":{"summary":"Update Call","tags":["Calls"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Call updated"}}}},"/availability/next-slot":{"get":{"summary":"Find Next Available Slot","tags":["Availability"],"responses":{"200":{"description":"Slot found"}}}},"/booking-resources/rooms":{"get":{"summary":"List Booking Rooms","tags":["Booking Resources"],"responses":{"200":{"description":"Rooms retrieved"}}}},"/booking-resources/equipment":{"get":{"summary":"List Booking Equipment","tags":["Booking Resources"],"responses":{"200":{"description":"Equipment retrieved"}}}},"/membership-tiers":{"get":{"summary":"List Membership Tiers","tags":["Memberships"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Tiers retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Membership Tier","tags":["Memberships"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Tier created"}}}},"/membership-tiers/{id}":{"get":{"summary":"Get Membership Tier","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Tier retrieved"}}},"patch":{"summary":"Update Membership Tier","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Tier updated"}}},"delete":{"summary":"Delete Membership Tier","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Tier deleted"}}}},"/customer-memberships":{"get":{"summary":"List Customer Memberships","tags":["Memberships"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Memberships retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Subscribe Customer","tags":["Memberships"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Subscription created"}}}},"/customer-memberships/{id}":{"get":{"summary":"Get Customer Membership","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Membership retrieved"}}},"patch":{"summary":"Update Membership","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Membership updated"}}}},"/customer-memberships/{id}/pause":{"post":{"summary":"Pause Membership","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Membership paused"}}}},"/customer-memberships/{id}/resume":{"post":{"summary":"Resume Membership","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Membership resumed"}}}},"/customer-memberships/{id}/cancel":{"post":{"summary":"Cancel Membership","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Membership cancelled"}}}},"/customer-memberships/{id}/billing-history":{"get":{"summary":"Get Billing History","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Billing history retrieved"}}}},"/membership-billing-history":{"get":{"summary":"List Billing History","tags":["Memberships"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"History retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}}},"/membership-billing-history/{id}":{"get":{"summary":"Get Billing Entry","tags":["Memberships"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Entry retrieved"}}}},"/commission-schedules":{"get":{"summary":"List Commission Schedules","description":"Returns all commission schedules for the authenticated store. By default only\nactive schedules are returned. Each schedule is enriched with the detail object\nthat matches its `schedule_type` (`flat_fee`, `percentage`, or `tiered`).\n","tags":["Commissions"],"parameters":[{"name":"include_inactive","in":"query","description":"When `true`, inactive schedules are included in the response","schema":{"type":"boolean","default":false},"example":false}],"responses":{"200":{"description":"List of commission schedules retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CommissionSchedule"}},"examples":{"flatFeeList":{"summary":"Single FLAT_FEE schedule","value":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","store_id":"s1t2u3v4-w5x6-7890-yzab-cd1234567890","name":"Retail Flat Fee","schedule_type":"FLAT_FEE","applies_to":"BOTH","active":true,"notes":"","created_at":"2026-04-28T10:00:00Z","updated_at":"2026-04-28T10:00:00Z","flat_fee":{"id":"f1e2d3c4-b5a6-7890-abcd-ef1234567890","schedule_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","flat_amount":25,"minimum_ticket_value":50},"percentage":null,"tiered":null}]}}}}},"401":{"description":"Unauthorized – invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create Commission Schedule","description":"Creates a new commission schedule and its associated detail record.\n\n- For `FLAT_FEE` schedules, provide a `flat_fee` object.\n- For `PERCENTAGE` schedules, provide a `percentage` object.\n- For `TIERED` schedules, provide a `tiered` object with a `tiers` array.\n\nThe newly created schedule, enriched with its detail object, is returned.\n","tags":["Commissions"],"requestBody":{"required":true,"description":"Commission schedule creation payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommissionScheduleRequest"},"examples":{"flatFee":{"summary":"FLAT_FEE schedule","value":{"name":"Retail Flat Fee","schedule_type":"FLAT_FEE","applies_to":"BOTH","active":true,"notes":"","flat_fee":{"flat_amount":25,"minimum_ticket_value":50}}},"percentage":{"summary":"PERCENTAGE schedule","value":{"name":"Standard 10% Commission","schedule_type":"PERCENTAGE","applies_to":"SALES","active":true,"notes":"","percentage":{"percentage_rate":0.1,"calculated_on":"GROSS","minimum_ticket_value":0,"maximum_commission_cap":500}}},"tiered":{"summary":"TIERED schedule","value":{"name":"Tiered Performance Commission","schedule_type":"TIERED","applies_to":"BOTH","active":true,"notes":"Resets monthly","tiered":{"tiers":[{"tier_label":"Bronze","threshold_from":0,"threshold_to":999.99,"commission_type":"PERCENTAGE","rate":0.05},{"tier_label":"Silver","threshold_from":1000,"threshold_to":4999.99,"commission_type":"PERCENTAGE","rate":0.08},{"tier_label":"Gold","threshold_from":5000,"threshold_to":null,"commission_type":"PERCENTAGE","rate":0.12}]}}}}}}},"responses":{"201":{"description":"Commission schedule created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionSchedule"},"examples":{"flatFee":{"summary":"Created FLAT_FEE schedule","value":{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","store_id":"s1t2u3v4-w5x6-7890-yzab-cd1234567890","name":"Retail Flat Fee","schedule_type":"FLAT_FEE","applies_to":"BOTH","active":true,"notes":"","created_at":"2026-04-28T10:00:00Z","updated_at":"2026-04-28T10:00:00Z","flat_fee":{"id":"f1e2d3c4-b5a6-7890-abcd-ef1234567890","schedule_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","flat_amount":25,"minimum_ticket_value":50},"percentage":null,"tiered":null}}}}}},"400":{"description":"Bad request – `name` or `schedule_type` is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized – invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/commission-schedules/{id}":{"get":{"summary":"Get Commission Schedule","description":"Returns a single commission schedule by ID, enriched with its detail object.","tags":["Commissions"],"parameters":[{"name":"id","in":"path","required":true,"description":"Unique commission schedule identifier","schema":{"type":"string","format":"uuid"},"example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}],"responses":{"200":{"description":"Commission schedule retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionSchedule"},"examples":{"percentage":{"summary":"PERCENTAGE schedule","value":{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","store_id":"s1t2u3v4-w5x6-7890-yzab-cd1234567890","name":"Standard 10% Commission","schedule_type":"PERCENTAGE","applies_to":"SALES","active":true,"notes":"","created_at":"2026-04-28T10:00:00Z","updated_at":"2026-04-28T10:00:00Z","flat_fee":null,"percentage":{"id":"p2q3r4s5-t6u7-8901-vwxy-z12345678901","schedule_id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","percentage_rate":0.1,"calculated_on":"GROSS","minimum_ticket_value":0,"maximum_commission_cap":500},"tiered":null}}}}}},"401":{"description":"Unauthorized – invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Commission schedule not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update Commission Schedule","description":"Partially updates a commission schedule. The `schedule_type` is immutable after\ncreation. Only the detail object matching the schedule's existing `schedule_type`\nwill be applied. For TIERED schedules, providing a `tiered.tiers` array replaces\n**all** existing tiers.\n","tags":["Commissions"],"parameters":[{"name":"id","in":"path","required":true,"description":"Unique commission schedule identifier","schema":{"type":"string","format":"uuid"},"example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}],"requestBody":{"required":true,"description":"Fields to update on the commission schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommissionScheduleRequest"},"examples":{"deactivate":{"summary":"Deactivate a schedule","value":{"active":false,"notes":"Suspended pending policy review"}},"updateFlatFee":{"summary":"Raise the flat fee amount","value":{"flat_fee":{"flat_amount":30,"minimum_ticket_value":75}}},"replaceTiers":{"summary":"Replace tiers on a TIERED schedule","value":{"tiered":{"tiers":[{"tier_label":"Bronze","threshold_from":0,"threshold_to":1499.99,"commission_type":"PERCENTAGE","rate":0.06},{"tier_label":"Gold","threshold_from":1500,"threshold_to":null,"commission_type":"PERCENTAGE","rate":0.1}]}}}}}}},"responses":{"200":{"description":"Commission schedule updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionSchedule"}}}},"401":{"description":"Unauthorized – invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Commission schedule not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete Commission Schedule","description":"Soft-deletes a commission schedule by setting `active` to `false`.\nThe record is retained in the database and can be retrieved by passing\n`include_inactive=true` to the list endpoint.\n","tags":["Commissions"],"parameters":[{"name":"id","in":"path","required":true,"description":"Unique commission schedule identifier","schema":{"type":"string","format":"uuid"},"example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}],"responses":{"204":{"description":"Commission schedule deactivated successfully (no content)"},"401":{"description":"Unauthorized – invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Commission schedule not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/communications":{"get":{"summary":"List Communications","tags":["Communications"],"responses":{"200":{"description":"Communications retrieved"}}},"post":{"summary":"Create Communication","tags":["Communications"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Communication created"}}}},"/communications/{id}":{"get":{"summary":"Get Communication","tags":["Communications"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Communication retrieved"}}}},"/calendar-holds":{"get":{"summary":"List Calendar Holds","tags":["Calendar"],"responses":{"200":{"description":"Holds retrieved"}}}},"/calendar-holds/{id}":{"patch":{"summary":"Update Calendar Hold","tags":["Calendar"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Hold updated"}}}},"/calendar-holds/counts":{"get":{"summary":"Get Hold Counts","tags":["Calendar"],"responses":{"200":{"description":"Counts retrieved"}}}},"/calendar-integration/accounts":{"get":{"summary":"List Calendar Accounts","tags":["Calendar"],"responses":{"200":{"description":"Accounts retrieved"}}}},"/calendar-integration/accounts/{id}":{"delete":{"summary":"Disconnect Calendar Account","tags":["Calendar"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Account disconnected"}}}},"/form-deliveries":{"get":{"summary":"List Form Deliveries","tags":["Forms"],"responses":{"200":{"description":"Deliveries retrieved"}}},"post":{"summary":"Create Form Delivery","tags":["Forms"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Delivery created"}}}},"/form-deliveries/{id}":{"patch":{"summary":"Update Form Delivery","tags":["Forms"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Delivery updated"}}}},"/notification-logs":{"get":{"summary":"List Notification Logs","tags":["Notifications"],"responses":{"200":{"description":"Logs retrieved"}}},"post":{"summary":"Create Notification Log","tags":["Notifications"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Log created"}}}},"/check-in/bookings/{id}/generate-link":{"post":{"summary":"Generate Check-In Link","tags":["Check-In"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Link generated"}}}},"/check-in/bookings/{id}/send-link":{"post":{"summary":"Send Check-In Link","tags":["Check-In"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Link sent"}}}},"/check-in/validate-token/{token}":{"get":{"summary":"Validate Check-In Token","tags":["Check-In"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Token valid"}}}},"/check-in/process":{"post":{"summary":"Process Check-In","tags":["Check-In"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Check-in processed"}}}},"/timeout-test":{"post":{"summary":"Timeout Test","tags":["System"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Test completed"}}}},"/auth/register":{"post":{"summary":"Register Customer","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Customer registered"}}}},"/auth/login":{"post":{"summary":"Unified Login","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Login successful"}}}},"/auth/logout":{"post":{"summary":"Logout","tags":["Authentication"],"responses":{"200":{"description":"Logout successful"}}}},"/auth/refresh":{"post":{"summary":"Refresh Token","tags":["Authentication"],"responses":{"200":{"description":"Token refreshed"}}}},"/auth/me":{"get":{"summary":"Get Current User","tags":["Authentication"],"responses":{"200":{"description":"User retrieved"}}}},"/auth/change-password":{"post":{"summary":"Change Password","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Password changed"}}}},"/auth/request-password-reset":{"post":{"summary":"Request Password Reset","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Reset requested"}}}},"/auth/reset-password":{"post":{"summary":"Reset Password","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Password reset"}}}},"/auth/verify-email":{"post":{"summary":"Verify Email","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Email verified"}}}},"/auth/logout-all":{"post":{"summary":"Logout All Sessions","tags":["Authentication"],"responses":{"200":{"description":"All sessions logged out"}}}},"/auth/api-keys":{"get":{"summary":"List API Keys","tags":["Authentication"],"responses":{"200":{"description":"API keys retrieved"}}},"post":{"summary":"Create API Key","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"API key created"}}}},"/auth/api-keys/{id}":{"delete":{"summary":"Delete API Key","tags":["Authentication"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"API key deleted"}}}},"/auth/api-keys/{id}/revoke":{"patch":{"summary":"Revoke API Key","tags":["Authentication"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"API key revoked"}}}},"/auth/verify":{"get":{"summary":"Verify API Key","tags":["Authentication"],"responses":{"200":{"description":"API key valid"}}}},"/auth/debug":{"get":{"summary":"Debug Authentication","tags":["Authentication"],"responses":{"200":{"description":"Debug info retrieved"}}}},"/services":{"get":{"summary":"List Services","tags":["Services"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/OffsetParam"},{"$ref":"#/components/parameters/PageParam"}],"responses":{"200":{"description":"Services retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedGenericResponse"}}}}}},"post":{"summary":"Create Service","tags":["Services"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Service created"}}}},"/services/debug":{"get":{"summary":"Debug Services Schema","tags":["Services"],"responses":{"200":{"description":"Debug info retrieved"}}}},"/services/grouped":{"get":{"summary":"Grouped Services","tags":["Services"],"responses":{"200":{"description":"Services retrieved"}}}},"/services/search":{"get":{"summary":"Search Services","tags":["Services"],"responses":{"200":{"description":"Search results"}}}},"/services/categories":{"get":{"summary":"Service Categories","tags":["Services"],"responses":{"200":{"description":"Categories retrieved"}}}},"/services/specialties":{"get":{"summary":"List Specialties","tags":["Services"],"responses":{"200":{"description":"Specialties retrieved"}}}},"/services/specialties/professionals":{"get":{"summary":"Professionals by Specialty","tags":["Services"],"responses":{"200":{"description":"Professionals retrieved"}}}},"/services/specialties/recommendations":{"get":{"summary":"Specialty Recommendations","tags":["Services"],"responses":{"200":{"description":"Recommendations retrieved"}}}},"/services/{id}":{"get":{"summary":"Get Service","tags":["Services"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Service retrieved"}}},"patch":{"summary":"Update Service","tags":["Services"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Service updated"}}},"delete":{"summary":"Delete Service","tags":["Services"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Service deleted"}}}},"/platform/clinics/{id}/staff":{"get":{"summary":"List Clinic Staff","tags":["Platform Clinics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Staff retrieved"}}},"post":{"summary":"Create Staff Member","tags":["Platform Clinics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Staff created"}}}},"/platform/clinics/{id}/roles":{"get":{"summary":"List Clinic Roles","tags":["Platform Clinics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Roles retrieved"}}},"post":{"summary":"Create Role","tags":["Platform Clinics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Role created"}}}},"/platform/clinics/{id}/roles/{roleId}":{"patch":{"summary":"Update Role Permissions","tags":["Platform Clinics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"roleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Role updated"}}}},"/platform/orgs/{id}":{"get":{"summary":"Get Organization","tags":["Platform Orgs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Organization retrieved"}}}},"/platform/orgs/{id}/clinics":{"get":{"summary":"List Organization Clinics","tags":["Platform Orgs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Clinics retrieved"}}}},"/platform/consent-signatures":{"get":{"summary":"List Consent Signatures","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"patient_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"encounter_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"consent_type","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["captured","declined","revoked","expired"]}},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Consent signature list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ConsentSignature"}}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}},"post":{"summary":"Create Consent Signature","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentSignatureCreate"}}}},"responses":{"201":{"description":"Consent signature created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentSignature"}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Encounter not found for patient"}}}},"/platform/consent-signatures/{id}":{"get":{"summary":"Get Consent Signature","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Consent signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentSignature"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not found"}}}},"/platform/medical-history":{"get":{"summary":"List Medical History","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"patient_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"encounter_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"category","in":"query","schema":{"type":"string","enum":["condition","allergy","medication","surgery","family_history","social_history","contraindication","note"]}},{"name":"status","in":"query","schema":{"type":"string","enum":["active","resolved","inactive","entered_in_error"]}},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Medical history list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MedicalHistoryRecord"}}}}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}},"post":{"summary":"Create Medical History Record","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MedicalHistoryCreate"}}}},"responses":{"201":{"description":"Medical history record created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MedicalHistoryRecord"}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Encounter not found for patient"}}}},"/platform/medical-history/{id}":{"get":{"summary":"Get Medical History Record","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Medical history record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MedicalHistoryRecord"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not found"}}},"patch":{"summary":"Update Medical History Record","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MedicalHistoryUpdate"}}}},"responses":{"200":{"description":"Medical history record updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MedicalHistoryRecord"}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not found"}}}},"/platform/event-types":{"get":{"summary":"List Platform Event Types","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"producer","in":"query","schema":{"type":"string"}},{"name":"active","in":"query","schema":{"type":"boolean"}},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Platform event type catalog","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlatformEventType"}}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}},"post":{"summary":"Create Platform Event Type","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEventTypeCreate"}}}},"responses":{"201":{"description":"Platform event type created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEventType"}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"409":{"description":"event_type already exists for this clinic"}}}},"/platform/event-types/{id}":{"get":{"summary":"Get Platform Event Type","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Platform event type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEventType"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not found"}}}},"/platform/events":{"get":{"summary":"List Platform Events","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"patient_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"encounter_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"event_type","in":"query","schema":{"type":"string"}},{"name":"aggregate_type","in":"query","schema":{"type":"string"}},{"name":"aggregate_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Platform event list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlatformEvent"}}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}},"post":{"summary":"Emit Platform Event","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEventCreate"}}}},"responses":{"201":{"description":"Platform event emitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEvent"}}}},"400":{"description":"Validation error or event_type not registered for this clinic"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Encounter not found"}}}},"/platform/events/{id}":{"get":{"summary":"Get Platform Event","tags":["Platform Clinical"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Platform event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEvent"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not found"}}},"patch":{"summary":"PATCH not supported","tags":["Platform Clinical"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"405":{"description":"Platform events are append-only. PATCH is not supported."}}},"delete":{"summary":"DELETE not supported","tags":["Platform Clinical"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"405":{"description":"Platform events are append-only. DELETE is not supported."}}}},"/docs/ui":{"x-internal":true,"x-base-path-note":"Mounted at the root (without /v1 prefix) — access as https://nopos.vercel.app/docs/ui","get":{"summary":"Swagger UI","description":"**Note:** The `/docs` tree is mounted at root (without `/v1` prefix).\nActual URLs: `https://nopos.vercel.app/docs/ui`, `/docs/openapi.json`, etc.\n","tags":["Documentation"],"responses":{"200":{"description":"UI served"}}}},"/docs/openapi.json":{"get":{"summary":"OpenAPI JSON","tags":["Documentation"],"responses":{"200":{"description":"Spec returned"}}}},"/docs/openapi.yaml":{"get":{"summary":"OpenAPI YAML","tags":["Documentation"],"responses":{"200":{"description":"Spec returned"}}}},"/docs/planning-ai.json":{"get":{"summary":"Planning AI Clinical Contract","tags":["Documentation"],"responses":{"200":{"description":"Clinical planning contract returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}}}}},"/docs/postman":{"get":{"summary":"Postman Collection","tags":["Documentation"],"responses":{"200":{"description":"Collection returned"}}}},"/auth/admin/register":{"post":{"summary":"Admin Register User","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"User created"}}}},"/auth/admin/create-store":{"post":{"summary":"Admin Create Store","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"201":{"description":"Store created"}}}},"/customer-auth/change-password":{"post":{"summary":"Customer Change Password","tags":["Customer Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Password changed"}}}},"/customer-auth/request-password-reset":{"post":{"summary":"Customer Request Password Reset","tags":["Customer Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Reset requested"}}}},"/customer-auth/reset-password":{"post":{"summary":"Customer Reset Password","tags":["Customer Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonObject"}}}},"responses":{"200":{"description":"Password reset"}}}},"/classes/{id}/book/{booking_id}":{"delete":{"summary":"Cancel Class Booking","tags":["Classes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Booking cancelled"}}}},"/calendar-integration/google/connect":{"get":{"summary":"Connect Google Calendar","tags":["Calendar"],"responses":{"200":{"description":"OAuth URL returned"}}}},"/calendar-integration/google/callback":{"get":{"summary":"Google OAuth Callback","tags":["Calendar"],"responses":{"200":{"description":"Connected"}}}},"/calendar-integration/outlook/connect":{"get":{"summary":"Connect Outlook Calendar","tags":["Calendar"],"responses":{"200":{"description":"OAuth URL returned"}}}},"/calendar-integration/outlook/callback":{"get":{"summary":"Outlook OAuth Callback","tags":["Calendar"],"responses":{"200":{"description":"Connected"}}}},"/docs/redoc":{"get":{"summary":"Redoc Documentation","tags":["Documentation"],"responses":{"200":{"description":"Documentation served"}}}},"/webhooks":{"get":{"summary":"List Webhooks","description":"List webhook subscriptions for the authenticated store.","tags":["Webhooks"],"responses":{"200":{"description":"Webhooks retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create Webhook","description":"Create a webhook subscription for one or more event types.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookInput"}}}},"responses":{"201":{"description":"Webhook created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Invalid webhook payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}":{"get":{"summary":"Get Webhook","description":"Fetch a webhook subscription by ID.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Webhook retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Invalid webhook ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update Webhook","description":"Update mutable webhook subscription fields.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook UUID","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdate"}}}},"responses":{"200":{"description":"Webhook updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Invalid update payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete Webhook","description":"Delete a webhook subscription for the authenticated store.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Webhook deleted"},"400":{"description":"Invalid webhook ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/deliveries":{"get":{"summary":"List Webhook Deliveries","description":"List recent delivery attempts for a webhook subscription.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook UUID","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Webhook deliveries retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListResponse"}}}},"400":{"description":"Invalid webhook ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/test":{"post":{"summary":"Test Webhook","description":"Send a signed webhook.test event and record the delivery attempt.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Webhook test attempted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}}},"400":{"description":"Invalid webhook ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/twilio/sms/inbound":{"post":{"summary":"Twilio Inbound SMS Webhook","description":"Receives inbound SMS/MMS events from Twilio after X-Twilio-Signature verification.","tags":["Twilio Webhooks"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"From":{"type":"string"},"To":{"type":"string"},"Body":{"type":"string"},"MessageSid":{"type":"string"},"NumMedia":{"type":"string"}},"required":["From","To"]}}}},"responses":{"200":{"description":"TwiML acknowledgement","content":{"text/xml":{"schema":{"$ref":"#/components/schemas/TwilioWebhookAcknowledgement"}}}},"403":{"description":"Invalid Twilio signature"}}}},"/twilio/sms/status":{"post":{"summary":"Twilio SMS Status Webhook","description":"Receives SMS delivery status callbacks from Twilio after signature verification.","tags":["Twilio Webhooks"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"MessageSid":{"type":"string"},"MessageStatus":{"type":"string"},"To":{"type":"string"},"From":{"type":"string"},"ErrorCode":{"type":"string"}},"required":["MessageSid","MessageStatus"]}}}},"responses":{"200":{"description":"TwiML acknowledgement","content":{"text/xml":{"schema":{"$ref":"#/components/schemas/TwilioWebhookAcknowledgement"}}}},"403":{"description":"Invalid Twilio signature"}}}},"/twilio/voice/inbound":{"post":{"summary":"Twilio Inbound Voice Webhook","description":"Receives inbound voice call webhooks from Twilio after signature verification.","tags":["Twilio Webhooks"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"From":{"type":"string"},"To":{"type":"string"},"CallSid":{"type":"string"},"CallStatus":{"type":"string"}},"required":["From","To","CallSid"]}}}},"responses":{"200":{"description":"TwiML call instructions","content":{"text/xml":{"schema":{"$ref":"#/components/schemas/TwilioWebhookAcknowledgement"}}}},"403":{"description":"Invalid Twilio signature"}}}},"/twilio/voice/status":{"post":{"summary":"Twilio Voice Status Webhook","description":"Receives voice call status callbacks from Twilio after signature verification.","tags":["Twilio Webhooks"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"CallSid":{"type":"string"},"CallStatus":{"type":"string"},"To":{"type":"string"},"From":{"type":"string"}},"required":["CallSid","CallStatus"]}}}},"responses":{"200":{"description":"TwiML acknowledgement","content":{"text/xml":{"schema":{"$ref":"#/components/schemas/TwilioWebhookAcknowledgement"}}}},"403":{"description":"Invalid Twilio signature"}}}},"/owner-alert-rules":{"get":{"summary":"List Owner Alert Rules","description":"Returns all threshold-based alert rules configured for the authenticated store.","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Alert rules retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OwnerAlertRule"}}}}}}},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Create Owner Alert Rule","description":"Creates a new threshold-based alert rule for the store.","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerAlertRuleInput"}}}},"responses":{"201":{"description":"Alert rule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerAlertRule"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"}}}},"/owner-alert-rules/{id}":{"patch":{"summary":"Update Owner Alert Rule","description":"Updates threshold, phone, snooze, or active status of an alert rule.","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerAlertRulePatch"}}}},"responses":{"200":{"description":"Alert rule updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerAlertRule"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Alert rule not found"}}},"delete":{"summary":"Delete Owner Alert Rule","description":"Removes an alert rule from the store.","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Alert rule deleted"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Alert rule not found"}}}},"/owner-alerts/fire":{"post":{"summary":"Fire an Owner Alert","description":"Evaluates a rule (snooze/active checks), sends an SMS via Twilio when\nTWILIO_* env vars are set, and logs the delivery to owner_alert_log.\n","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerAlertFireInput"}}}},"responses":{"200":{"description":"Alert suppressed (rule is snoozed or inactive)"},"201":{"description":"Alert fired and logged","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"$ref":"#/components/schemas/OwnerAlertLogEntry"},"twilio_sid":{"type":"string","nullable":true},"simulated":{"type":"boolean"}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Rule not found"}}}},"/owner-alerts/log":{"get":{"summary":"Get Owner Alert Log","description":"Retrieves alert delivery history for the store with optional filters.","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"rule_id","in":"query","description":"Filter by alert rule UUID","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","description":"ISO 8601 start timestamp (inclusive)","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","description":"ISO 8601 end timestamp (inclusive)","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","description":"Maximum number of rows to return (max 500)","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Alert log retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OwnerAlertLogEntry"}}}}}}},"401":{"description":"Missing or invalid API key"}}}},"/owner-alerts/reply":{"post":{"summary":"Handle Owner SMS Reply","description":"Parses an inbound owner reply (OK / STOP / SNOOZE), updates\nowner_alert_log.reply_action, and applies the action to the rule\n(deactivate on STOP, set snooze_until on SNOOZE).\n","tags":["Owner Alerts"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerAlertReplyInput"}}}},"responses":{"200":{"description":"Reply processed","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"$ref":"#/components/schemas/OwnerAlertLogEntry"},"reply_action":{"type":"string","enum":["ok","stop","snooze"]},"rule_update":{"type":"object","nullable":true}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Alert log entry not found"}}}},"/staff-tasks":{"get":{"summary":"List Staff Task Templates","description":"Returns all task templates for the authenticated store.","tags":["Staff Tasks"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"is_active","in":"query","description":"Filter by active/inactive status","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Task templates retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StaffTask"}}}}}}},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Create Staff Task Template","description":"Creates a new shift-level task template for the store.","tags":["Staff Tasks"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTaskInput"}}}},"responses":{"201":{"description":"Task template created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTask"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"}}}},"/staff-tasks/{id}":{"patch":{"summary":"Update Staff Task Template","description":"Updates or deactivates a task template.","tags":["Staff Tasks"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTaskInput"}}}},"responses":{"200":{"description":"Task template updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTask"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Task template not found"}}},"delete":{"summary":"Delete Staff Task Template","description":"Permanently deletes a task template.","tags":["Staff Tasks"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Task template deleted"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Task template not found"}}}},"/staff-task-assignments":{"get":{"summary":"List Staff Task Assignments","description":"Returns assignments for the store with optional filters.","tags":["Staff Task Assignments"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["pending","acknowledged","escalated","skipped"]}},{"name":"staff_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"task_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"date","in":"query","description":"Filter by due_at date (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Assignments retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StaffTaskAssignment"}}}}}}},"400":{"description":"Invalid query parameter"},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Create Staff Task Assignment","description":"Creates a new task assignment (called by the reminder engine).","tags":["Staff Task Assignments"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTaskAssignmentInput"}}}},"responses":{"201":{"description":"Assignment created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTaskAssignment"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"}}}},"/staff-task-assignments/{id}":{"patch":{"summary":"Acknowledge or Escalate Assignment","description":"Updates the status or escalation step of a task assignment. Setting status to 'acknowledged' auto-stamps acknowledged_at if not provided.","tags":["Staff Task Assignments"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTaskAssignmentPatch"}}}},"responses":{"200":{"description":"Assignment updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffTaskAssignment"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Assignment not found"}}}},"/v1/staff-members":{"get":{"summary":"List Staff Members","description":"Returns all staff members for the authenticated store.","tags":["Staff Members"],"parameters":[{"name":"is_active","in":"query","description":"Filter by active status","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Staff members retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StaffMember"}}}}}}},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Create a Staff Member","description":"Creates a new staff member for the authenticated store.","tags":["Staff Members"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMemberCreate"}}}},"responses":{"201":{"description":"Staff member created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"}}}},"/v1/staff-members/{id}":{"patch":{"summary":"Update a Staff Member","description":"Updates rate, role, active status, or contact info for a staff member.","tags":["Staff Members"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMemberPatch"}}}},"responses":{"200":{"description":"Staff member updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Staff member not found"}}}},"/v1/staff-shifts":{"get":{"summary":"List Staff Shifts","description":"Returns scheduled shifts with optional filters by date range and staff member.","tags":["Staff Shifts"],"parameters":[{"name":"staff_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"shift_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"status","in":"query","schema":{"type":"string","enum":["scheduled","confirmed","no_show","covered","cancelled"]}}],"responses":{"200":{"description":"Shifts retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StaffShift"}}}}}}},"400":{"description":"Invalid query parameter"},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Create a Staff Shift","description":"Creates a new scheduled shift for a staff member.","tags":["Staff Shifts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffShiftCreate"}}}},"responses":{"201":{"description":"Shift created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffShift"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"}}}},"/v1/staff-shifts/coverage-gaps":{"get":{"summary":"Coverage Gaps","description":"Returns shifts with status='scheduled' where no CLOCK_IN time_entry exists\nwithin 15 minutes of the shift start_time. Primary signal for the labor agent.\n","tags":["Staff Shifts"],"parameters":[{"name":"shift_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Coverage gaps retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CoverageGap"}},"total_gaps":{"type":"integer"}}}}}},"400":{"description":"Invalid query parameter"},"401":{"description":"Missing or invalid API key"}}}},"/v1/staff-shifts/{id}":{"patch":{"summary":"Update a Staff Shift","description":"Updates status (confirm, mark no-show, etc.), times, or notes for a shift.","tags":["Staff Shifts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffShiftPatch"}}}},"responses":{"200":{"description":"Shift updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffShift"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Shift not found"}}},"delete":{"summary":"Delete a Staff Shift","description":"Removes a scheduled shift.","tags":["Staff Shifts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Shift deleted"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Shift not found"}}}},"/v1/reports/labor-forecast":{"get":{"summary":"Labor Cost Forecast","description":"Returns scheduled hours, estimated labor cost, and labor% vs projected revenue\nfor a date range. Flags staff members with overtime risk (> 40 scheduled hours).\nprojected_revenue can be passed as a query param or will be derived from the\nsame-day-last-week payment average.\n","tags":["Labor Forecast"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"projected_revenue","in":"query","schema":{"type":"number","format":"float"}}],"responses":{"200":{"description":"Labor forecast computed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaborForecast"}}}},"400":{"description":"Missing or invalid from/to parameters"},"401":{"description":"Missing or invalid API key"}}}},"/v1/reports/owner-summary":{"get":{"summary":"Owner Executive Summary","description":"Multi-location financial roll-up for the requested period. Computes per-location\nrevenue, order count, avg cover, labor hours/cost/%, food cost/%, gross profit\nand a totals row across all included stores. Degrades gracefully (0) when labor\nor food cost data is absent. The caller's API key scopes accessible stores;\npass store_ids to filter to a subset.\n","tags":["Owner Reports"],"parameters":[{"name":"from_date","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"Start of the reporting period (inclusive), YYYY-MM-DD"},{"name":"to_date","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"End of the reporting period (inclusive), YYYY-MM-DD"},{"name":"store_ids","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated list of store UUIDs to include; omit for all stores under the API key"}],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Owner summary computed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerSummary"}}}},"400":{"description":"Missing or invalid from_date / to_date / store_ids"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Requested store_ids are not accessible with this API key"}}}},"/v1/reports/daily-briefing":{"get":{"summary":"Daily Owner Briefing","description":"Returns a pre-formatted text string suitable for SMS or email delivery by the\nHermes agent. Defaults to yesterday when no date range is supplied. Covers the\nsame store scope as owner-summary.\n","tags":["Owner Reports"],"parameters":[{"name":"from_date","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Start of the reporting period (defaults to yesterday)"},{"name":"to_date","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"End of the reporting period (defaults to yesterday)"},{"name":"store_ids","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated list of store UUIDs; omit for all stores under the API key"}],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Daily briefing text generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DailyBriefing"}}}},"400":{"description":"Invalid from_date or to_date"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Requested store_ids are not accessible with this API key"}}}},"/v1/shift-checklists":{"get":{"summary":"List Shift Checklist Templates","description":"Returns all shift checklist templates for the store.","tags":["Shift Checklists"],"parameters":[{"name":"shift_type","in":"query","schema":{"type":"string","enum":["opening","pre_service","closing","custom"]}},{"name":"is_active","in":"query","schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"List of checklist templates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ShiftChecklist"}}}}}}},"400":{"description":"Invalid filter parameter"},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Create Shift Checklist Template","description":"Creates a new shift checklist template with optional items.","tags":["Shift Checklists"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftChecklistCreate"}}}},"responses":{"201":{"description":"Checklist created","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ShiftChecklist"},{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ShiftChecklistItem"}}}}]}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"}}}},"/v1/shift-checklists/missed-today":{"get":{"summary":"Missed Runs Today","description":"Returns all checklist runs with status=missed for today. Used by the owner alert agent.","tags":["Shift Checklists"],"responses":{"200":{"description":"Missed runs for today","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ShiftChecklistRun"}},"date":{"type":"string","format":"date"}}}}}},"401":{"description":"Missing or invalid API key"}}}},"/v1/shift-checklists/{id}/items":{"get":{"summary":"List Items for a Checklist","description":"Returns all items for a specific checklist template.","tags":["Shift Checklists"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"List of checklist items","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ShiftChecklistItem"}}}}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Checklist not found"}}}},"/v1/shift-checklist-runs/runs":{"get":{"summary":"List Checklist Runs","description":"Returns checklist runs with optional filters by date and status.","tags":["Shift Checklists"],"parameters":[{"name":"shift_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"status","in":"query","schema":{"type":"string","enum":["in_progress","completed","missed"]}},{"name":"checklist_id","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"List of checklist runs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ShiftChecklistRun"}}}}}}},"400":{"description":"Invalid filter parameter"},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Start a Checklist Run","description":"Creates a new run for a checklist template for a given shift date.","tags":["Shift Checklists"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftChecklistRunCreate"}}}},"responses":{"201":{"description":"Run created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftChecklistRun"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Checklist not found"}}}},"/v1/shift-checklist-runs/runs/{id}":{"patch":{"summary":"Update Checklist Run Status","description":"Mark a run as completed or missed.","tags":["Shift Checklists"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["in_progress","completed","missed"]}}}}}},"responses":{"200":{"description":"Run updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftChecklistRun"}}}},"400":{"description":"Invalid status"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Run not found"}}}},"/v1/shift-checklist-runs/runs/{id}/complete-item":{"post":{"summary":"Check Off an Item","description":"Marks an individual item as completed within a run. Auto-completes the run when all items are checked.","tags":["Shift Checklists"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftChecklistCompleteItem"}}}},"responses":{"201":{"description":"Item completion recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftChecklistCompletion"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Run or item not found"}}}},"/reviews/ingest":{"post":{"summary":"Ingest External Reviews","description":"Upsert one or more reviews from an external platform (called by the Hermes polling agent). Uses ON CONFLICT (store_id, platform, external_id) to handle re-polling without creating duplicates. After upsert, sets alert_fired=true and logs a negative-review event for any review with rating <= 2.\n","tags":["Reviews"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reviews"],"properties":{"reviews":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReviewInput"}}}}}}},"responses":{"200":{"description":"Reviews upserted","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"integer"},"upserted":{"type":"integer"},"negative_alerts_queued":{"type":"integer"}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Forbidden"}}}},"/reviews/needs-reply":{"get":{"summary":"List Reviews Needing Reply","description":"Returns reviews with rating <= 2 and no reply_body yet. This is the primary queue for the Guest Concierge agent.\n","tags":["Reviews"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"platform","in":"query","description":"Filter by platform","schema":{"type":"string","enum":["google","yelp","tripadvisor","custom"]}},{"name":"limit","in":"query","description":"Maximum number of reviews to return (max 500)","schema":{"type":"integer","default":500}}],"responses":{"200":{"description":"Reviews needing reply","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReview"}}}}}}},"400":{"description":"Invalid query parameter"},"401":{"description":"Missing or invalid API key"}}}},"/reviews/summary":{"get":{"summary":"Review Reputation Summary","description":"Returns aggregate reputation metrics: average rating per platform, total reviews, percentage negative, and overall stats. Optionally filtered by date range.\n","tags":["Reviews"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"from","in":"query","description":"Start of date range (ISO 8601 timestamp)","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","description":"End of date range (ISO 8601 timestamp)","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Reputation summary","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ReviewSummary"}}}}}},"401":{"description":"Missing or invalid API key"}}}},"/reviews":{"get":{"summary":"List Reviews","description":"Returns store reviews with optional filters for platform, rating, date range, and alert_fired status.\n","tags":["Reviews"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"platform","in":"query","description":"Filter by platform","schema":{"type":"string","enum":["google","yelp","tripadvisor","custom"]}},{"name":"rating","in":"query","description":"Filter by exact star rating (1-5)","schema":{"type":"integer","minimum":1,"maximum":5}},{"name":"from","in":"query","description":"Filter reviews on or after this timestamp","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","description":"Filter reviews on or before this timestamp","schema":{"type":"string","format":"date-time"}},{"name":"alert_fired","in":"query","description":"Filter by whether the owner alert has been fired","schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Maximum rows to return (max 500)","schema":{"type":"integer","default":500}}],"responses":{"200":{"description":"List of reviews","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReview"}}}}}}},"400":{"description":"Invalid query parameter"},"401":{"description":"Missing or invalid API key"}}}},"/reviews/{id}/reply":{"patch":{"summary":"Save Review Reply","description":"Save a reply body for the review and record the replied_at timestamp.","tags":["Reviews"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reply_body"],"properties":{"reply_body":{"type":"string","example":"Thank you for your feedback. We are sorry to hear about your experience."}}}}}},"responses":{"200":{"description":"Reply saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReview"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Review not found"}}}},"/reviews/{id}/sentiment":{"patch":{"summary":"Update Review Sentiment Score","description":"Update the sentiment_score for a review. Called by the AI sentiment analysis step after ingest. Score must be between -1.0 and 1.0.\n","tags":["Reviews"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sentiment_score"],"properties":{"sentiment_score":{"type":"number","minimum":-1,"maximum":1,"example":-0.72}}}}}},"responses":{"200":{"description":"Sentiment score updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReview"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Review not found"}}}},"/v1/pos-connectors":{"get":{"summary":"List POS Connectors","description":"Returns all external POS connectors registered for this store. Credentials are never included in the response.\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Connectors retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PosConnector"}}}}}}},"401":{"description":"Missing or invalid API key"}}},"post":{"summary":"Register POS Connector","description":"Register a new external POS connector for this store. Each store may have at most one connector per provider. Credentials are stored encrypted and never returned in GET responses.\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PosConnectorCreateRequest"}}}},"responses":{"201":{"description":"Connector created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PosConnector"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"409":{"description":"Connector for this provider already exists"}}}},"/v1/pos-connectors/{id}":{"patch":{"summary":"Update POS Connector","description":"Update credentials, location_id, or is_active flag on an existing connector. At least one field must be provided.\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PosConnectorPatchRequest"}}}},"responses":{"200":{"description":"Connector updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PosConnector"}}}},"400":{"description":"Invalid input or no fields provided"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Connector not found"}}},"delete":{"summary":"Delete POS Connector","description":"Remove a POS connector. All associated sync log entries are also deleted via ON DELETE CASCADE.\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Connector deleted"},"400":{"description":"Invalid connector id"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Connector not found"}}}},"/v1/pos-connectors/{id}/sync":{"post":{"summary":"Trigger POS Sync","description":"Trigger an on-demand sync for the specified connector. Pulls orders from the external POS for the given date window and normalises them into NOPOS orders/order_items. Records the attempt in pos_sync_log. Provider calls are stubbed (TODO) — no real API calls are made yet.\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"from_date":{"type":"string","format":"date","description":"Inclusive start of sync window (YYYY-MM-DD). Defaults to today."},"to_date":{"type":"string","format":"date","description":"Inclusive end of sync window (YYYY-MM-DD). Defaults to today."}}}}}},"responses":{"200":{"description":"Sync attempt completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PosSyncTriggerResult"}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Connector not found"},"409":{"description":"Connector is inactive or a sync is already in progress"}}}},"/v1/pos-connectors/{id}/sync-log":{"get":{"summary":"Get Sync Log","description":"Retrieve the sync history for a connector, ordered by most recent first. Supports an optional limit query parameter (max 200).\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Sync log retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PosSyncLogEntry"}}}}}}},"400":{"description":"Invalid connector id"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Connector not found"}}}},"/v1/reports/consolidated":{"get":{"summary":"Consolidated Cross-Connector Revenue Report","description":"Aggregates NOPOS native orders together with all active external connector data for a given date range. Intended for the Hermes report agent and the owner dashboard. Groups revenue totals by source (nopos, toast, square, clover, custom).\n","tags":["POS Connectors"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"from_date","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"Start of the reporting period (inclusive), YYYY-MM-DD"},{"name":"to_date","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"End of the reporting period (inclusive), YYYY-MM-DD"}],"responses":{"200":{"description":"Consolidated report computed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"from_date":{"type":"string","format":"date"},"to_date":{"type":"string","format":"date"},"total_revenue":{"type":"number","format":"double"},"total_orders":{"type":"integer"},"by_source":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","example":"square"},"order_count":{"type":"integer"},"revenue":{"type":"number","format":"double"}}}}}}}}},"400":{"description":"Missing or invalid from_date/to_date"},"401":{"description":"Missing or invalid API key"}}}}},"tags":[{"name":"Health","description":"API health and status endpoints"},{"name":"Locations","description":"Location management operations — canonical alias for Stores endpoints"},{"name":"Stores","description":"Store management operations (compatibility name; prefer Locations)"},{"name":"Settings","description":"Store settings management"},{"name":"Products","description":"Product and service management"},{"name":"Customers","description":"Customer management"},{"name":"Orders","description":"Order processing"},{"name":"Payments","description":"Payment processing"},{"name":"Subscriptions","description":"Customer subscription management"},{"name":"Bookings","description":"Booking management"},{"name":"Content","description":"Content management"},{"name":"Professionals","description":"Professional/provider management"},{"name":"Authentication","description":"Authentication and API key management"},{"name":"Upload","description":"File upload operations"},{"name":"Slides","description":"Slide management for presentations and content"},{"name":"Images","description":"AI image generation and storage"},{"name":"Images Async","description":"Asynchronous image generation with job tracking"},{"name":"Webhooks","description":"Webhook subscription and delivery history management"},{"name":"Twilio Webhooks","description":"Inbound Twilio callback endpoints with signature verification"},{"name":"Time Entries","description":"Staff time tracking"},{"name":"Work Orders","description":"Task and work order management"},{"name":"Waitlist","description":"Patient waitlist management"},{"name":"Treatment Notes","description":"Clinical SOAP notes"},{"name":"Platform Auth","description":"Staff/EHR authentication with RBAC"},{"name":"AI","description":"AI content and image generation"},{"name":"Calendar","description":"Calendar blocks and holds"},{"name":"Capacity","description":"Resource capacity management"},{"name":"Customer Auth","description":"Customer authentication"},{"name":"Users","description":"Customer profile management"},{"name":"Inventory","description":"Product inventory tracking"},{"name":"Categories","description":"Product categories"},{"name":"Classes","description":"Scheduled classes and group sessions"},{"name":"Estimates","description":"Quote and estimate management"},{"name":"Gift Cards","description":"Gift card issuance and redemption"},{"name":"Loyalty","description":"Loyalty program management"},{"name":"Packages","description":"Service packages and bundles"},{"name":"Patient Vitals","description":"Patient vital signs tracking"},{"name":"Benefits","description":"Benefits and coupon management"},{"name":"Calls","description":"AI call handling"},{"name":"Availability","description":"Availability checking"},{"name":"Booking Resources","description":"Rooms and equipment resources"},{"name":"Memberships","description":"Membership tiers and subscriptions"},{"name":"Commissions","description":"Commission schedule management"},{"name":"Communications","description":"Communication logging"},{"name":"Forms","description":"Form delivery tracking"},{"name":"Notifications","description":"Notification logging"},{"name":"Check-In","description":"Self check-in system"},{"name":"System","description":"System utilities"},{"name":"Services","description":"Service catalog management"},{"name":"Platform Clinics","description":"Clinic staff and role management"},{"name":"Platform Orgs","description":"Organization management"},{"name":"Owner Alerts","description":"Threshold-based owner SMS alert rules and delivery log"},{"name":"Staff Tasks","description":"Staff task template management (reminder engine)"},{"name":"Staff Task Assignments","description":"Staff task assignment tracking and acknowledgment"},{"name":"Documentation","description":"API documentation serving"},{"name":"Staff Members","description":"Staff directory with roles and hourly rates"},{"name":"Staff Shifts","description":"Scheduled shifts CRUD and coverage-gap intelligence"},{"name":"Labor Forecast","description":"Scheduled labor cost, labor-pct, and overtime risk report"},{"name":"Shift Checklists","description":"Shift checklist templates, runs, and item completion tracking"},{"name":"Reviews","description":"External review ingestion and reputation monitoring (Google, Yelp, TripAdvisor)"},{"name":"Owner Reports","description":"Owner executive summary and daily briefing reports (multi-location financial roll-up)"},{"name":"POS Connectors","description":"External POS connector registry and sync log (Toast, Square, Clover, custom)"},{"name":"Accounting","description":"Double-entry general ledger — chart of accounts and immutable journal (ADR-0003)"},{"name":"Platform Clinical","description":"Clinical workflows — consent signatures, medical history, platform event type catalog, and append-only platform event bus"}]}