{"openapi":"3.2.0","info":{"title":"GuideLab API","version":"1.0.0","description":"REST API for GuideLab clinics, labs, workflows, finance, files, and integrations."},"servers":[{"url":"https://api-stage.guidelab.co"}],"components":{"schemas":{},"parameters":{},"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"__Secure-guidelab-prod.session_token","description":"Production session cookie set for the guidelab.co domain by Better Auth"},"bearerAuth":{"type":"http","scheme":"bearer","description":"Versioned GuideLab native-device credential (`gl_device_v1_*`) issued by `POST /auth/device/v1/sign-in`; Better Auth browser session tokens are cookie-only."},"organizationApiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Organization-owned service key issued by the owner-only `/auth/service-keys` endpoint. Currently accepted only on shipment operations and constrained by `shipments:read` / `shipments:write` permissions."}}},"paths":{"/files":{"get":{"tags":["Files"],"operationId":"listFiles","summary":"List files","description":"Returns a paginated list of files for the current organization. Labs see all files; clinics see only their own. Supports filtering by patient, clinic, file type, and unassigned status.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":10000,"default":1,"description":"Page number","example":1},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","description":"Filter by patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by patient ID","name":"patientId","in":"query"},{"schema":{"type":"string","description":"Filter by clinic ID (lab only)","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by clinic ID (lab only)","name":"clinicId","in":"query"},{"schema":{"type":"string","description":"Filter by file type: scan, photo, prescription, xray, other","example":"scan"},"required":false,"description":"Filter by file type: scan, photo, prescription, xray, other","name":"type","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"description":"Show only files not attached to an order","example":"false"},"required":false,"description":"Show only files not attached to an order","name":"unassigned","in":"query"},{"schema":{"type":"string","description":"Search by file name","example":"upper_scan"},"required":false,"description":"Search by file name","name":"search","in":"query"}],"responses":{"200":{"description":"Paginated list of files with patient and order info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":["string","null"]},"uploadedByOrganizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"},"thumbnailUrl":{"type":["string","null"]},"patientId":{"type":["string","null"]},"patientName":{"type":["string","null"]},"orderId":{"type":["string","null"]},"orderDisplayId":{"type":["string","null"]},"orderStatus":{"type":["string","null"]},"uploadedBy":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","partnershipId","uploadedByOrganizationId","name","type","mimeType","size","url","thumbnailUrl","patientId","patientName","orderId","orderDisplayId","orderStatus","uploadedBy","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized — valid session required"}}}},"/files/upload":{"post":{"tags":["Files"],"operationId":"uploadFile","summary":"Upload a file","description":"Uploads a small file directly via multipart form data. Large files must use the presigned upload/finalize flow. Optionally attach to a patient, order, or order item. A thumbnail can be uploaded alongside the main file.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"201":{"description":"File uploaded and record created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":["string","null"]},"uploadedByOrganizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"},"thumbnailUrl":{"type":["string","null"]},"orderId":{"type":["string","null"]},"patientId":{"type":["string","null"]},"orderItemId":{"type":["string","null"]},"fileRequirementId":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","partnershipId","uploadedByOrganizationId","name","type","mimeType","size","url","thumbnailUrl","orderId","patientId","orderItemId","fileRequirementId","createdAt"]}}}},"400":{"description":"Missing file, invalid type, or file too large"},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Clinic standalone storage limit exceeded"}}}},"/files/presign":{"post":{"tags":["Files"],"operationId":"presignFileUpload","summary":"Get presigned upload URL","description":"Generates a presigned PUT URL so the client can upload directly to storage. No file DB row is created until the client calls finalize after a successful upload. Optionally returns a second signed PUT URL for a thumbnail. Valid for 5 minutes.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string","minLength":1},"contentType":{"type":"string"},"size":{"type":"integer","exclusiveMinimum":0,"maximum":1073741824},"orderId":{"type":"string"},"patientId":{"type":"string"},"orderItemId":{"type":"string"},"fileRequirementId":{"type":"string"},"treatmentPhaseId":{"type":"string"},"orderPhaseId":{"type":"string"},"type":{"type":"string","enum":["scan","photo","prescription","xray","other"]},"thumbnailSize":{"type":"integer","exclusiveMinimum":0,"maximum":52428800}},"required":["filename","size"],"additionalProperties":false}}}},"responses":{"201":{"description":"Presigned upload URL(s) and finalize token returned","content":{"application/json":{"schema":{"type":"object","properties":{"fileId":{"type":"string"},"uploadUrl":{"type":"string"},"uploadContentType":{"type":"string"},"thumbnailUploadUrl":{"type":["string","null"]},"thumbnailUploadContentType":{"type":["string","null"]},"finalizeToken":{"type":"string"}},"required":["fileId","uploadUrl","uploadContentType","thumbnailUploadUrl","thumbnailUploadContentType","finalizeToken"]}}}},"400":{"description":"Missing required fields, invalid file type, or file too large"},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Clinic standalone storage limit exceeded"},"429":{"description":"Upload issuance budget exceeded"}}}},"/files/finalize":{"post":{"tags":["Files"],"operationId":"finalizeFileUpload","summary":"Finalize a direct file upload","description":"Confirms that a previously presigned upload completed successfully, validates the stored object metadata and size, and then creates the file DB record.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"finalizeToken":{"type":"string","minLength":1}},"required":["finalizeToken"]}}}},"responses":{"200":{"description":"File record created or already finalized","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":["string","null"]},"uploadedByOrganizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"},"thumbnailUrl":{"type":["string","null"]},"orderId":{"type":["string","null"]},"patientId":{"type":["string","null"]},"orderItemId":{"type":["string","null"]},"fileRequirementId":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","partnershipId","uploadedByOrganizationId","name","type","mimeType","size","url","thumbnailUrl","orderId","patientId","orderItemId","fileRequirementId","createdAt"]}}}},"400":{"description":"Invalid finalize token or upload is incomplete"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Upload belongs to a different user"},"404":{"description":"Referenced order or patient not found"},"409":{"description":"Upload finalization conflicted"}}}},"/files/copy-to-order":{"post":{"tags":["Files"],"operationId":"copyFilesToOrder","summary":"Copy files to an order","description":"Copies existing patient file objects to unique storage keys and associates the copies with a different order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fileIds":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":20},"orderId":{"type":"string"},"patientId":{"type":"string"},"fileRequirementId":{"type":"string"},"treatmentPhaseId":{"type":"string"}},"required":["fileIds","orderId"]}}}},"responses":{"201":{"description":"New file records created for the target order","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":"string"},"uploadedByOrganizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"},"thumbnailUrl":{"type":["string","null"]},"orderId":{"type":"string"},"patientId":{"type":["string","null"]},"fileRequirementId":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","partnershipId","uploadedByOrganizationId","name","type","mimeType","size","url","thumbnailUrl","orderId","patientId","fileRequirementId","createdAt"]}}},"required":["files"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"No matching files or order not found"},"409":{"description":"A file belongs to a different partnership epoch"},"413":{"description":"The requested copies exceed the aggregate byte limit"},"429":{"description":"Copy budget exceeded"},"502":{"description":"A source object could not be copied"}}}},"/files/reassign":{"post":{"tags":["Files"],"operationId":"reassignFiles","summary":"Reassign files between orders","description":"Moves files from one order to another, updating order item and file requirement associations. S3 objects stay in place — only DB records are updated.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fromOrderId":{"type":"string"},"toOrderId":{"type":"string"},"assignments":{"type":"array","items":{"type":"object","properties":{"fileId":{"type":"string"},"orderItemId":{"type":["string","null"]},"fileRequirementId":{"type":["string","null"]},"treatmentPhaseId":{"type":["string","null"]}},"required":["fileId"]}}},"required":["fromOrderId","toOrderId","assignments"]}}}},"responses":{"200":{"description":"Files reassigned successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"reassigned":{"type":"number"}},"required":["success","reassigned"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Destination order not found"},"409":{"description":"A delivery-proof file cannot be reassigned"}}}},"/files/{id}":{"delete":{"tags":["Files"],"operationId":"deleteFile","summary":"Delete a file","description":"Archives (soft-deletes) a file: the record and its R2 object are retained, and a retention job physically purges them after the retention window. Only an owner of the owning organization may archive; delivery-proof files and files attached to non-draft orders are immutable.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"File ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"File ID","name":"id","in":"path"}],"responses":{"200":{"description":"File archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"File ID is required"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — owner role required"},"404":{"description":"File not found or not owned by current organization"},"409":{"description":"File is retained, attached to a non-draft order, or its partnership epoch is no longer writable"}}}},"/files/{id}/download":{"get":{"tags":["Files"],"operationId":"downloadFile","summary":"Download a file","description":"Streams the file content directly from R2 through the Worker (R2 egress is free; this avoids presigned-URL redirects and CORS surprises). Pass `?thumbnail=true` for the thumbnail variant.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"File ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"File ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"],"description":"Return thumbnail variant","example":"false"},"required":false,"description":"Return thumbnail variant","name":"thumbnail","in":"query"}],"responses":{"200":{"description":"File content streamed directly from R2","content":{"application/octet-stream":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"File not found or not owned by current organization"}}}},"/orders":{"get":{"operationId":"listOrders","tags":["Orders"],"summary":"List orders","description":"Returns a paginated list of orders for the authenticated user's organization. Supports filtering by status, date range, search, and other criteria. Labs see orders where they are the lab; clinics see orders where they are the clinic.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":10000,"default":1,"description":"Page number","example":1},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","enum":["full","compact"],"default":"full","description":"Response projection. The backward-compatible default returns the full order shape; compact omits detail-only fields for list UIs.","example":"compact"},"required":false,"description":"Response projection. The backward-compatible default returns the full order shape; compact omits detail-only fields for list UIs.","name":"view","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Opaque cursor returned by the previous non-search page. Send it with the matching page, filters, and sort. Search results continue to use page-based pagination."},"required":false,"description":"Opaque cursor returned by the previous non-search page. Send it with the matching page, filters, and sort. Search results continue to use page-based pagination.","name":"cursor","in":"query"},{"schema":{"type":"string","description":"Comma-separated status filter","example":"submitted,active"},"required":false,"description":"Comma-separated status filter","name":"status","in":"query"},{"schema":{"type":"string","description":"Comma-separated order type filter (digital, physical)","example":"digital,physical"},"required":false,"description":"Comma-separated order type filter (digital, physical)","name":"type","in":"query"},{"schema":{"type":"string","description":"Search by order number, patient name, doctor name, or partner org","example":"ORD-2025"},"required":false,"description":"Search by order number, patient name, doctor name, or partner org","name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","dueDate","deadline","submittedAt","orderNumber","status"],"default":"createdAt","description":"Field to sort by","example":"createdAt"},"required":false,"description":"Field to sort by","name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","description":"Sort direction","example":"desc"},"required":false,"description":"Sort direction","name":"sortOrder","in":"query"},{"schema":{"type":"string","description":"Filter by clinic ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by clinic ID","name":"clinicId","in":"query"},{"schema":{"type":"string","description":"Filter by patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by patient ID","name":"patientId","in":"query"},{"schema":{"type":"string","description":"Filter by doctor user ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by doctor user ID","name":"doctorUserId","in":"query"},{"schema":{"type":"string","description":"Filter by lab ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by lab ID","name":"labId","in":"query"},{"schema":{"type":"string","description":"Filter by doctor name","example":"Dr. Smith"},"required":false,"description":"Filter by doctor name","name":"doctorName","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"description":"Filter orders that have files","example":"true"},"required":false,"description":"Filter orders that have files","name":"hasFiles","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"description":"Filter orders with pending phase advance requests","example":"false"},"required":false,"description":"Filter orders with pending phase advance requests","name":"hasAdvanceRequest","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"description":"Include completed and cancelled orders. Archived orders are always excluded.","example":"false"},"required":false,"description":"Include completed and cancelled orders. Archived orders are always excluded.","name":"includeAll","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"description":"Include submittedCount and onHoldCount in response metadata (org-wide, unfiltered)","example":"true"},"required":false,"description":"Include submittedCount and onHoldCount in response metadata (org-wide, unfiltered)","name":"withBadgeCounts","in":"query"}],"responses":{"200":{"description":"Paginated list of orders with metadata","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"},"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["page","limit","total","totalPages","hasMore","nextCursor"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"}}}},"/orders/filter-options":{"get":{"operationId":"getOrderFilterOptions","tags":["Orders"],"summary":"Get filter options for orders","description":"Returns available filter options for the order list, including distinct doctor names, partner organizations (labs for clinics, clinics for labs), and patients (clinics only). Used to populate filter dropdowns in the UI. Each list is capped; `truncated` reports which lists were cut short so the UI can offer a search instead of a partial dropdown.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Filter options including doctors, partners, and patients","content":{"application/json":{"schema":{"type":"object","properties":{"doctors":{"type":"array","items":{"type":"string"}},"patients":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"required":["id","firstName","lastName"]}},"labs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"clinics":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"truncated":{"type":"object","properties":{"doctors":{"type":"boolean"},"patients":{"type":"boolean"},"partners":{"type":"boolean"}},"required":["doctors","patients","partners"]}},"required":["doctors","truncated"]}}}},"401":{"description":"Unauthorized"}}}},"/orders/needs-approval":{"get":{"operationId":"getOrdersNeedingApproval","tags":["Orders"],"summary":"Get orders needing approval","description":"Returns a paginated list of submitted orders awaiting lab approval, with line items, files, work tray assignments, and unread message counts. Lab only. Supports fuzzy search across order number, PAN number, doctor name, clinic name, and patient name.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Page number (default: 1)","example":"1"},"required":false,"description":"Page number (default: 1)","name":"page","in":"query"},{"schema":{"type":"string","description":"Items per page (default: 20, max: 50)","example":"20"},"required":false,"description":"Items per page (default: 20, max: 50)","name":"limit","in":"query"},{"schema":{"type":"string","description":"Fuzzy search term","example":"John"},"required":false,"description":"Fuzzy search term","name":"search","in":"query"}],"responses":{"200":{"description":"Paginated list of submitted orders with items, files, and unread counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can view orders needing approval"}}}},"/orders/shipped":{"get":{"operationId":"getShippedOrders","tags":["Orders"],"summary":"Get dispatched orders","description":"Returns orders with a dispatched shipment and every consignment associated with each order. Shipment state is independent from the canonical order lifecycle.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":50,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"Paginated shipped orders","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"type":{"type":"string","enum":["digital","physical"]},"dueDate":{"type":["string","null"]},"doctorName":{"type":["string","null"]},"patient":{"type":["object","null"],"properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"required":["id","firstName","lastName"]},"clinic":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"clinicLocation":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"items":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"productNames":{"type":"array","items":{"type":"string"}}},"required":["count","productNames"]},"shipments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"shipmentNumber":{"type":"integer","exclusiveMinimum":0},"status":{"type":"string","enum":["draft","packing","ready","in_transit","delivery_exception","delivered","returned","cancelled"]},"shippingMethod":{"type":"string","enum":["carrier","local_driver"]},"labels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"packageId":{"type":"string"},"packageNumber":{"type":"integer","exclusiveMinimum":0},"carrier":{"type":["string","null"]},"trackingNumber":{"type":["string","null"]},"trackingUrl":{"type":["string","null"],"format":"uri"}},"required":["id","packageId","packageNumber","carrier","trackingNumber","trackingUrl"]}},"driverName":{"type":["string","null"]},"driverPhone":{"type":["string","null"]},"shippedAt":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"deliveredAt":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"address":{"type":"string"},"city":{"type":"string"},"postcode":{"type":"string"},"country":{"type":"string"},"contactName":{"type":["string","null"]},"contactPhone":{"type":["string","null"]},"signedBy":{"type":["string","null"]}},"required":["id","shipmentNumber","status","shippingMethod","labels","driverName","driverPhone","shippedAt","deliveredAt","address","city","postcode","country","contactName","contactPhone","signedBy"]}}},"required":["id","orderNumber","status","type","dueDate","doctorName","patient","clinic","clinicLocation","items","shipments"]}},"pagination":{"type":"object","properties":{"page":{"type":"integer","exclusiveMinimum":0},"limit":{"type":"integer","exclusiveMinimum":0},"total":{"type":"integer","minimum":0},"totalPages":{"type":"integer","minimum":0}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can view shipped orders"}}}},"/orders/calendar":{"get":{"operationId":"getOrderCalendar","tags":["Orders"],"summary":"Get calendar data","description":"Returns order counts and summaries grouped by date for a given date range. Used for calendar views to show how many orders are due or have deadlines on each day. Excludes drafts and archived orders. Supports searching by order number, patient name, doctor, or partner org.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Start date (YYYY-MM-DD)","example":"2025-01-01"},"required":true,"description":"Start date (YYYY-MM-DD)","name":"startDate","in":"query"},{"schema":{"type":"string","description":"End date (YYYY-MM-DD)","example":"2025-01-31"},"required":true,"description":"End date (YYYY-MM-DD)","name":"endDate","in":"query"},{"schema":{"type":"string","description":"Date field to use: dueDate (default) or deadline","example":"dueDate"},"required":false,"description":"Date field to use: dueDate (default) or deadline","name":"dateField","in":"query"},{"schema":{"type":"string","description":"Search term for order number, patient, doctor, or org name","example":"John"},"required":false,"description":"Search term for order number, patient, doctor, or org name","name":"search","in":"query"}],"responses":{"200":{"description":"Calendar data grouped by date with order counts and summaries","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"object","additionalProperties":{"type":"object","properties":{"total":{"type":"number"},"orders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"patientName":{"type":["string","null"]},"status":{"type":"string"}},"required":["id","orderNumber","patientName","status"]}}},"required":["total","orders"]}}},"required":["days"]}}}},"400":{"description":"Invalid query parameters - dates must be YYYY-MM-DD format"},"401":{"description":"Unauthorized"}}}},"/orders/calendar/day":{"get":{"operationId":"getOrderCalendarDay","tags":["Orders"],"summary":"Get orders for a specific day","description":"Returns a paginated list of orders for a specific date with full order details including phase summaries. Supports filtering by status and search. Used for the day detail view in the calendar.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Date (YYYY-MM-DD)","example":"2025-01-15"},"required":true,"description":"Date (YYYY-MM-DD)","name":"date","in":"query"},{"schema":{"type":"string","description":"Date field to use: dueDate (default) or deadline","example":"dueDate"},"required":false,"description":"Date field to use: dueDate (default) or deadline","name":"dateField","in":"query"},{"schema":{"type":"string","description":"Comma-separated status filter","example":"submitted,active"},"required":false,"description":"Comma-separated status filter","name":"status","in":"query"},{"schema":{"type":"string","description":"Search term","example":"John"},"required":false,"description":"Search term","name":"search","in":"query"},{"schema":{"type":"string","description":"Page number (default: 1)","example":"1"},"required":false,"description":"Page number (default: 1)","name":"page","in":"query"},{"schema":{"type":"string","description":"Items per page (default: 20, max: 100)","example":"20"},"required":false,"description":"Items per page (default: 20, max: 100)","name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of orders for the day with phase summaries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"}}}},"/orders/drafts":{"get":{"operationId":"getMostRecentDraft","tags":["Orders"],"summary":"Get most recent draft order","description":"Returns the most recent draft order created by the authenticated user within their organization. Includes related clinic, lab, patient, and line item data. Returns null if no draft exists.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Most recent draft order with related data, or null if none exists","content":{"application/json":{"schema":{"type":"object","properties":{"draft":{}}}}}},"401":{"description":"Unauthorized"}}},"post":{"operationId":"createDraftOrder","tags":["Orders"],"summary":"Create draft order","description":"Creates a new draft order with optional line items and wizard state for multi-step order creation. The draft is owned by the authenticated user and can be resumed later.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clinicId":{"type":"string"},"labId":{"type":"string"},"clinicLocationId":{"type":"string"},"patientId":{"type":"string"},"doctorName":{"type":"string"},"doctorEmail":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"doctorPhone":{"type":"string"},"type":{"type":"string","enum":["digital","physical"],"default":"digital"},"receivingSourceId":{"type":["string","null"],"minLength":1},"scannerType":{"type":"string","maxLength":100},"dueDate":{"type":"string"},"notes":{"type":"string","maxLength":5000},"hidePatientFromLab":{"type":"boolean","default":false},"items":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"toothGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","enum":["single","bridge"]},"teeth":{"type":"array","items":{"type":"object","properties":{"fdi":{"type":"integer"},"role":{"type":"string","enum":["crown","pontic"]},"support":{"type":"string","enum":["natural","implant"]},"implantSystemId":{"type":"string"},"implantPlatform":{"type":"string","maxLength":50}},"required":["fdi","role"]},"minItems":1}},"required":["id","type","teeth"]},"default":[]},"quantity":{"type":"integer","exclusiveMinimum":0,"default":1},"materialId":{"type":"string"},"material":{"type":"string","default":""},"shadeSystemId":{"type":"string"},"shadeOcclusal":{"type":"string","default":""},"shadeMiddle":{"type":"string","default":""},"shadeGingival":{"type":"string","default":""},"defaultImplantSystemId":{"type":"string"},"defaultImplantPlatform":{"type":"string","maxLength":50},"notes":{"type":"string","maxLength":5000,"default":""},"customFieldValues":{"type":"object","additionalProperties":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"fileIds":{"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false}},"bundleId":{"type":"string"},"bundleInstanceId":{"type":"string"},"bundleName":{"type":"string"},"bundlePrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["productId"]},"maxItems":100,"default":[]},"wizardState":{"type":"object","properties":{"step":{"type":"number","minimum":1,"maximum":6},"categoryId":{"type":"string"},"categoryName":{"type":"string"},"phasesToStart":{"type":"array","items":{"type":"string"}},"phaseNeededBy":{"type":"object","additionalProperties":{"type":"string"}}},"required":["step"]}}}}}},"responses":{"201":{"description":"Created draft order ID and order number","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"}},"required":["id","orderNumber"]}}}},"400":{"description":"Validation failed"},"401":{"description":"Unauthorized"}}}},"/orders/drafts/{id}":{"get":{"operationId":"getDraftById","tags":["Orders"],"summary":"Get specific draft order","description":"Returns a specific draft order by ID, including related clinic, lab, patient, line items, and attached files. Only accessible by the draft creator within their organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Draft order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Draft order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Draft order with items, files, and related data, or null if not found","content":{"application/json":{"schema":{"type":"object","properties":{"draft":{}}}}}},"400":{"description":"Draft ID is required"},"401":{"description":"Unauthorized"}}},"patch":{"operationId":"updateDraftOrder","tags":["Orders"],"summary":"Update draft order","description":"Updates an existing draft order's fields and optionally replaces all line items. Only the draft creator can update it. Supports updating wizard state for multi-step order flows.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Draft order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Draft order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clinicId":{"type":"string"},"labId":{"type":"string"},"clinicLocationId":{"type":"string"},"patientId":{"type":"string"},"doctorName":{"type":"string"},"doctorEmail":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"doctorPhone":{"type":"string"},"type":{"type":"string","enum":["digital","physical"]},"receivingSourceId":{"type":["string","null"],"minLength":1},"scannerType":{"type":"string","maxLength":100},"dueDate":{"type":["string","null"]},"notes":{"type":"string","maxLength":5000},"hidePatientFromLab":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"toothGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","enum":["single","bridge"]},"teeth":{"type":"array","items":{"type":"object","properties":{"fdi":{"type":"integer"},"role":{"type":"string","enum":["crown","pontic"]},"support":{"type":"string","enum":["natural","implant"]},"implantSystemId":{"type":"string"},"implantPlatform":{"type":"string","maxLength":50}},"required":["fdi","role"]},"minItems":1}},"required":["id","type","teeth"]},"default":[]},"quantity":{"type":"integer","exclusiveMinimum":0,"default":1},"materialId":{"type":"string"},"material":{"type":"string","default":""},"shadeSystemId":{"type":"string"},"shadeOcclusal":{"type":"string","default":""},"shadeMiddle":{"type":"string","default":""},"shadeGingival":{"type":"string","default":""},"defaultImplantSystemId":{"type":"string"},"defaultImplantPlatform":{"type":"string","maxLength":50},"notes":{"type":"string","maxLength":5000,"default":""},"customFieldValues":{"type":"object","additionalProperties":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"fileIds":{"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false}},"bundleId":{"type":"string"},"bundleInstanceId":{"type":"string"},"bundleName":{"type":"string"},"bundlePrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["productId"]},"maxItems":100},"wizardState":{"type":"object","properties":{"step":{"type":"number","minimum":1,"maximum":6},"categoryId":{"type":"string"},"categoryName":{"type":"string"},"phasesToStart":{"type":"array","items":{"type":"string"}},"phaseNeededBy":{"type":"object","additionalProperties":{"type":"string"}}},"required":["step"]}}}}}},"responses":{"200":{"description":"Confirmation that the draft was updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Validation failed"},"401":{"description":"Unauthorized"},"404":{"description":"Draft not found or not accessible"}}},"delete":{"operationId":"deleteDraftOrder","tags":["Orders"],"summary":"Delete draft order","description":"Permanently deletes a draft order and archives its clinical file records for durable retention cleanup. R2 objects remain tracked until the retention worker confirms deletion.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Draft order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Draft order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Confirmation that the draft was deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Draft ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Draft not found or not accessible"},"409":{"description":"Draft changed, is scanner-booked, or contains delivery-proof evidence"}}}},"/orders/bulk-delete":{"post":{"operationId":"bulkDeleteOrders","tags":["Orders"],"summary":"Bulk delete or cancel orders","description":"Processes a batch of order IDs in a single request. Drafts are deleted while their clinical files are archived for durable retention cleanup; non-draft orders are cancelled. Each order is processed independently.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":100}},"required":["ids"]}}}},"responses":{"200":{"description":"Counts of deleted/cancelled orders and per-id errors","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"number"},"cancelled":{"type":"number"},"total":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["id","error"]}}},"required":["deleted","cancelled","total","errors"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"}}}},"/orders/{id}/cancel":{"post":{"operationId":"cancelOrderWithRefundChoice","tags":["Orders"],"summary":"Cancel an order with an explicit refund decision","description":"Cancels an order. Paid orders require a lab owner/admin to choose a full, partial, or no refund and provide a reason. Stripe direct-charge refunds are committed only after provider confirmation.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"idempotencyKey":{"type":"string","minLength":8,"maxLength":200},"reason":{"type":"string","minLength":1,"maxLength":2000},"refundMode":{"type":"string","enum":["none","full","partial"]},"refundAmount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["idempotencyKey","reason","refundMode"],"additionalProperties":false}}}},"responses":{"200":{"description":"Order cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"refund":{"type":"object","properties":{"mode":{"type":"string","enum":["none","full","partial"]},"status":{"type":"string","enum":["not_requested","pending","succeeded","failed"]},"refundId":{"type":["string","null"]},"amount":{"type":["string","null"]},"currency":{"type":"string"}},"required":["mode","status","refundId","amount","currency"]}},"required":["message","refund"]}}}},"202":{"description":"Refund is pending provider confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"refund":{"type":"object","properties":{"mode":{"type":"string","enum":["none","full","partial"]},"status":{"type":"string","enum":["not_requested","pending","succeeded","failed"]},"refundId":{"type":["string","null"]},"amount":{"type":["string","null"]},"currency":{"type":"string"}},"required":["mode","status","refundId","amount","currency"]}},"required":["message","refund"]}}}},"400":{"description":"Invalid cancellation or refund amount"},"401":{"description":"Unauthorized"},"403":{"description":"Lab owner or admin access is required"},"404":{"description":"Order or paid receipt not found"},"409":{"description":"Order or idempotency state conflicts"},"502":{"description":"Stripe could not confirm the refund"},"503":{"description":"Stripe is not configured safely"}}}},"/orders/{id}":{"get":{"operationId":"getOrderDetail","tags":["Orders"],"summary":"Get order by ID","description":"Returns full order details including line items, phases, status history, phase step history, production tasks (lab only), and active hold information. Patient data is masked for labs when hidePatientFromLab is enabled.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full order details with items, phases, history, tasks, and active hold","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string"},"items":{"type":"array","items":{}},"phases":{"type":"array","items":{}},"statusHistory":{"type":"array","items":{}},"phaseStepHistory":{"type":"array","items":{}},"tasks":{"type":["array","null"],"items":{}},"activeHold":{}},"required":["id","orderNumber","status","items","phases","statusHistory","phaseStepHistory","tasks"],"additionalProperties":{}}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"},"500":{"description":"Submitted order catalog revision is invalid"}}},"patch":{"operationId":"updateOrder","tags":["Orders"],"summary":"Update order","description":"Updates order fields such as patient, doctor, dates, notes, and assignment. Labs can update any accessible order; clinics can only update orders in draft status. Assigning a user triggers a notification and auto-subscribes them.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clinicLocationId":{"type":["string","null"]},"patientId":{"type":["string","null"]},"doctorUserId":{"type":["string","null"]},"doctorName":{"type":"string"},"doctorPhone":{"type":"string"},"doctorEmail":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"type":{"type":"string","enum":["digital","physical"]},"receivingSourceId":{"type":["string","null"],"minLength":1},"scannerType":{"type":"string","maxLength":100},"panNumber":{"type":"string"},"manualFilePath":{"type":["string","null"]},"deadline":{"type":["string","null"]},"dueDate":{"type":["string","null"]},"notes":{"type":"string","maxLength":5000},"internalNotes":{"type":"string","maxLength":10000},"assignedToMemberId":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"Updated order record","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string"}},"required":["id","orderNumber","status"],"additionalProperties":{}}}}},"400":{"description":"Validation failed"},"401":{"description":"Unauthorized"},"403":{"description":"Only the destination lab can assign an order"},"404":{"description":"Order not found or cannot be edited"},"500":{"description":"Destination lab configuration is invalid"}}},"delete":{"operationId":"cancelOrder","tags":["Orders"],"summary":"Cancel order","description":"Cancels an order by setting its status to 'cancelled' and recording a status history entry. Labs can cancel any of their orders; clinics can only cancel orders in draft status.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Confirmation that the order was cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or cannot be deleted"},"409":{"description":"Paid order requires the explicit cancellation flow"}}}},"/orders/{id}/status":{"patch":{"operationId":"updateOrderStatus","tags":["Orders"],"summary":"Update order status","description":"Transitions an existing submitted order with validation of allowed transitions. Draft submission is owned exclusively by POST /orders/:id/submit so phase materialization and promotion remain atomic.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"notes":{"type":"string","maxLength":500}},"required":["status"]}}}},"responses":{"200":{"description":"Updated order with full status history","content":{"application/json":{"schema":{"type":"object","properties":{"order":{},"statusHistory":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fromStatus":{"type":["string","null"]},"toStatus":{"type":"string"},"changedBy":{"type":["string","null"]},"changedByName":{"type":["string","null"]},"changedByActorKind":{"type":"string","enum":["system","user","anonymized_user"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","fromStatus","toStatus","changedBy","changedByName","changedByActorKind","notes","createdAt"]}}},"required":["statusHistory"]}}}},"400":{"description":"Invalid status transition or incomplete phases for completion"},"401":{"description":"Unauthorized"},"403":{"description":"Clinics cannot perform this status transition"},"404":{"description":"Order not found"},"409":{"description":"Partnership/status conflict or insufficient inventory for completion"}}}},"/orders/{id}/submit":{"post":{"operationId":"submitDraftOrder","tags":["Orders"],"summary":"Submit an existing order draft","description":"Atomically validates and promotes the caller-owned draft in place. Replays return the same order when submitted_at proves a prior successful submission.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Existing draft order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Existing draft order ID","name":"id","in":"path"}],"responses":{"200":{"description":"The existing order draft was submitted or replayed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"replayed":{"type":"boolean"}},"required":["id","orderNumber","replayed"]}}}},"400":{"description":"Draft or catalog validation failed"},"401":{"description":"Unauthorized"},"402":{"description":"The destination lab cannot receive new work"},"404":{"description":"Draft not found or not accessible"},"409":{"description":"Draft lifecycle, partnership, or payment policy changed during submission"}}}},"/orders/{id}/status-history":{"get":{"operationId":"getOrderStatusHistory","tags":["Orders"],"summary":"Get order status history","description":"Returns the complete status transition history for an order, plus the phase-change history, sourced from the unified orderActivity log.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Order status and phase change history with change details","content":{"application/json":{"schema":{"type":"object","properties":{"statusHistory":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fromStatus":{"type":["string","null"]},"toStatus":{"type":"string"},"changedBy":{"type":["string","null"]},"changedByName":{"type":["string","null"]},"changedByActorKind":{"type":"string","enum":["system","user","anonymized_user"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","fromStatus","toStatus","changedBy","changedByName","changedByActorKind","notes","createdAt"]}},"phaseStepHistory":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"phaseName":{"type":["string","null"]},"fromPhase":{"type":["string","null"]},"toPhase":{"type":"string"},"changedBy":{"type":["string","null"]},"changedByName":{"type":["string","null"]},"changedByActorKind":{"type":"string","enum":["system","user","anonymized_user"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","phaseName","fromPhase","toPhase","changedBy","changedByName","changedByActorKind","notes","createdAt"]}}},"required":["statusHistory","phaseStepHistory"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"}}}},"/orders/{id}/hold":{"post":{"operationId":"createOrderHold","tags":["Orders"],"summary":"Put order on hold","description":"Places an order on hold with a specified hold reason from the lab's configured reasons. Optionally creates file reupload requirements if the hold reason requires it. Only labs can put orders on hold. Sends a notification to the clinic.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"holdReasonId":{"type":"string","minLength":1},"notes":{"type":"string","maxLength":2000},"fileIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":20}},"required":["holdReasonId"]}}}},"responses":{"200":{"description":"Created hold record with file reupload requirements","content":{"application/json":{"schema":{"type":"object","properties":{"hold":{},"requirements":{"type":"array","items":{}}},"required":["requirements"]}}}},"400":{"description":"Invalid status transition or file validation failed"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can put orders on hold"},"404":{"description":"Order or hold reason not found"}}}},"/orders/{id}/hold-requirements":{"get":{"operationId":"getOrderHoldRequirements","tags":["Orders"],"summary":"Get hold requirements for order","description":"Returns the active hold and its file reupload requirements for an order. Accessible by both labs and clinics with access to the order. Returns null if no active hold exists.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Active hold with file reupload requirements, or null if no active hold","content":{"application/json":{"schema":{"type":"object","properties":{"hold":{"type":["object","null"],"properties":{"id":{"type":"string"},"orderId":{"type":"string"},"holdReasonName":{"type":"string"},"notes":{"type":["string","null"]},"previousStatus":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"},"requirements":{"type":"array","items":{}}},"required":["id","orderId","holdReasonName","notes","previousStatus","status","createdAt","requirements"]},"requirements":{"type":"array","items":{}}},"required":["hold","requirements"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"}}}},"/orders/{id}/hold-requirements/{requirementId}/review":{"post":{"operationId":"reviewHoldRequirement","tags":["Orders"],"summary":"Review hold requirement","description":"Approves or rejects a submitted hold requirement. Only labs can review requirements. When all requirements are approved, the hold is automatically resolved and the order resumes its previous status.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Hold requirement ID","example":"7c9e6679-7425-40de-944b-e07fc1f90ae7"},"required":true,"description":"Hold requirement ID","name":"requirementId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["approved","rejected"]},"notes":{"type":"string","maxLength":2000}},"required":["decision"]}}}},"responses":{"200":{"description":"Review result including whether all requirements are approved and order auto-resumed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"allApproved":{"type":"boolean"},"autoResumed":{"type":"boolean"}},"required":["success","allApproved","autoResumed"]}}}},"400":{"description":"Requirement is not in submitted state"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can review hold requirements"},"404":{"description":"Order, requirement, or hold record not found"}}}},"/orders/{id}/hold-requirements/{requirementId}/submit":{"post":{"operationId":"submitHoldRequirement","tags":["Orders"],"summary":"Submit hold requirement","description":"Submits a file to fulfill a hold requirement. Only clinics can submit hold requirements. Supports resubmission after rejection. Sends a notification to the lab for review.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Hold requirement ID","example":"7c9e6679-7425-40de-944b-e07fc1f90ae7"},"required":true,"description":"Hold requirement ID","name":"requirementId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fileId":{"type":"string","minLength":1}},"required":["fileId"]}}}},"responses":{"200":{"description":"Confirmation that the requirement was submitted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Requirement is not in a state that accepts submissions"},"401":{"description":"Unauthorized"},"403":{"description":"Only clinics can submit hold requirements"},"404":{"description":"Order, requirement, or file not found"}}}},"/orders/{orderId}/phase-tasks":{"get":{"tags":["Orders"],"operationId":"listOrderPhaseTasks","summary":"List tasks of an order's current phase","description":"Returns the ordered task list of the order's active phase. For clinic requesters, only clinic-visible entries are returned and labels resolve to clinicTitle when set.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orderId","in":"path"}],"responses":{"200":{"description":"Phase tasks","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"integer"},"isCurrent":{"type":"boolean"}},"required":["id","taskId","label","sortOrder","isCurrent"]}}},"required":["tasks"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Order not found"}}}},"/orders/{id}/phases/{phaseId}/request-advance":{"post":{"operationId":"requestOrderPhaseAdvance","tags":["Orders"],"summary":"Request phase advancement (clinic only)","description":"Allows a clinic to submit files for a phase and ask the lab to begin work. Sets filesSubmitted and points currentPhaseTaskId at the phase's first task.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Order phase ID","example":"7c9e6679-7425-40de-944b-e07fc1f90ae7"},"required":true,"description":"Order phase ID","name":"phaseId","in":"path"}],"responses":{"200":{"description":"Confirmation that the phase advance was requested","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Files already submitted or no tasks configured for this phase"},"401":{"description":"Unauthorized"},"403":{"description":"Only clinics can request phase advancement"},"404":{"description":"Order or phase not found"},"409":{"description":"Order or phase state changed while updating"}}}},"/orders/{id}/phases/{phaseId}/work-tray":{"patch":{"operationId":"assignOrderPhaseWorkTray","tags":["Orders"],"summary":"Assign or unassign a work tray to a stage (lab only)","description":"Sets orderPhase.workTrayId for one stage. A tray can occupy at most one active stage at a time; pass null workTrayId to unassign.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"phaseId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workTrayId":{"type":["string","null"]}},"required":["workTrayId"]}}}},"responses":{"200":{"description":"Assigned","content":{"application/json":{"schema":{"type":"object","properties":{"workTrayId":{"type":["string","null"]},"workTray":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"color":{"type":["string","null"]}},"required":["id","code","color"]}},"required":["workTrayId","workTray"]}}}},"403":{"description":"Only labs can assign trays"},"404":{"description":"Order, stage, or tray not found"},"409":{"description":"Tray already assigned to another stage","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"conflict":{"type":"object","properties":{"orderPhaseId":{"type":"string"},"phaseName":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":"string"},"sameOrder":{"type":"boolean"}},"required":["orderPhaseId","phaseName","orderId","orderNumber","sameOrder"]}},"required":["error"]}}}}}}},"/orders/{id}/qc":{"get":{"operationId":"getOrderQc","tags":["Orders","Quality Control"],"summary":"Get an order's revision-aware QC state","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":20,"default":10},"required":false,"name":"historyLimit","in":"query"}],"responses":{"200":{"description":"QC configuration, current gate state, and bounded history","content":{"application/json":{"schema":{"type":"object","properties":{"qcEnabled":{"type":"boolean"},"selectAllEnabled":{"type":"boolean"},"fulfillmentRevision":{"type":"integer"},"gatePassed":{"type":"boolean"},"checklist":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["id","name","sortOrder"]}},"latestInspection":{"type":["object","null"],"properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderRevision":{"type":"integer"},"attemptNumber":{"type":"integer"},"outcome":{"type":"string","enum":["passed","failed"]},"notes":{"type":["string","null"]},"completedByUserId":{"type":["string","null"]},"completedByName":{"type":["string","null"]},"completedAt":{"type":"string"},"results":{"type":"array","items":{"type":"object","properties":{"checklistItemId":{"type":"string"},"itemName":{"type":"string"},"passed":{"type":"boolean"},"notes":{"type":["string","null"]}},"required":["checklistItemId","itemName","passed","notes"]}}},"required":["id","orderId","orderRevision","attemptNumber","outcome","notes","completedByUserId","completedByName","completedAt","results"]},"history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderRevision":{"type":"integer"},"attemptNumber":{"type":"integer"},"outcome":{"type":"string","enum":["passed","failed"]},"notes":{"type":["string","null"]},"completedByUserId":{"type":["string","null"]},"completedByName":{"type":["string","null"]},"completedAt":{"type":"string"},"results":{"type":"array","items":{"type":"object","properties":{"checklistItemId":{"type":"string"},"itemName":{"type":"string"},"passed":{"type":"boolean"},"notes":{"type":["string","null"]}},"required":["checklistItemId","itemName","passed","notes"]}}},"required":["id","orderId","orderRevision","attemptNumber","outcome","notes","completedByUserId","completedByName","completedAt","results"]}}},"required":["qcEnabled","selectAllEnabled","fulfillmentRevision","gatePassed","checklist","latestInspection","history"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Lab access required"},"404":{"description":"Order not found"},"409":{"description":"QC checklist exceeds its bounded execution limit"}}},"post":{"operationId":"completeOrderQc","tags":["Orders","Quality Control"],"summary":"Record an immutable QC inspection","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"checklistItemId":{"type":"string","minLength":1},"passed":{"type":"boolean"},"notes":{"type":["string","null"],"maxLength":2000}},"required":["checklistItemId","passed"]},"minItems":1,"maxItems":200},"notes":{"type":["string","null"],"maxLength":5000}},"required":["results"]}}}},"responses":{"201":{"description":"QC inspection recorded","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderRevision":{"type":"integer"},"attemptNumber":{"type":"integer"},"outcome":{"type":"string","enum":["passed","failed"]},"notes":{"type":["string","null"]},"completedByUserId":{"type":["string","null"]},"completedByName":{"type":["string","null"]},"completedAt":{"type":"string"},"results":{"type":"array","items":{"type":"object","properties":{"checklistItemId":{"type":"string"},"itemName":{"type":"string"},"passed":{"type":"boolean"},"notes":{"type":["string","null"]}},"required":["checklistItemId","itemName","passed","notes"]}}},"required":["id","orderId","orderRevision","attemptNumber","outcome","notes","completedByUserId","completedByName","completedAt","results"]}}}},"400":{"description":"Checklist result set is incomplete or invalid"},"401":{"description":"Unauthorized"},"403":{"description":"Lab access required"},"404":{"description":"Order not found"},"409":{"description":"QC is disabled or order is not active"}}}},"/orders/{id}/surgical-report":{"get":{"operationId":"getCurrentSurgicalReport","tags":["Orders"],"summary":"Get current surgical report review state","description":"Returns the immutable report revisions and review timeline for the active surgical-report hold.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Current surgical report state","content":{"application/json":{"schema":{"type":"object","properties":{"blockedByOtherHold":{"type":"boolean"},"review":{"type":["object","null"],"properties":{"reviewId":{"type":"string"},"holdId":{"type":"string"},"orderPhaseId":{"type":"string"},"orderPhaseTaskId":{"type":"string"},"status":{"type":"string","enum":["pending_review","changes_requested","approved"]},"currentRevisionNumber":{"type":"integer","exclusiveMinimum":0},"reviewNotes":{"type":["string","null"]},"bookingUrl":{"type":["string","null"]},"reviewedAt":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"currentRevision":{"type":"object","properties":{"id":{"type":"string"},"revisionNumber":{"type":"integer","exclusiveMinimum":0},"projectUrl":{"type":["string","null"]},"submittedByMemberId":{"type":"string"},"submittedAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"category":{"type":"string","enum":["report","extra"]},"sortOrder":{"type":"integer","minimum":0}},"required":["id","name","type","category","sortOrder"]}}},"required":["id","revisionNumber","projectUrl","submittedByMemberId","submittedAt","files"]},"revisions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"revisionNumber":{"type":"integer","exclusiveMinimum":0},"projectUrl":{"type":["string","null"]},"submittedByMemberId":{"type":"string"},"submittedAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"category":{"type":"string","enum":["report","extra"]},"sortOrder":{"type":"integer","minimum":0}},"required":["id","name","type","category","sortOrder"]}}},"required":["id","revisionNumber","projectUrl","submittedByMemberId","submittedAt","files"]}},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sequence":{"type":"integer","exclusiveMinimum":0},"eventType":{"type":"string","enum":["submitted","resubmitted","changes_requested","approved"]},"revisionId":{"type":"string"},"actorMemberId":{"type":"string"},"notes":{"type":["string","null"]},"createdAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"}},"required":["id","sequence","eventType","revisionId","actorMemberId","notes","createdAt"]}}},"required":["reviewId","holdId","orderPhaseId","orderPhaseTaskId","status","currentRevisionNumber","reviewNotes","bookingUrl","reviewedAt","currentRevision","revisions","events"]}},"required":["blockedByOtherHold","review"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Order not found"}}}},"/orders/{id}/surgical-report/{reviewId}/approve":{"post":{"operationId":"approveSurgicalReport","tags":["Orders"],"summary":"Approve surgical report (clinic only)","description":"Approves the current immutable revision, completes the approval task, resolves its hold, and resumes production.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"reviewId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","format":"uuid"}},"required":["commandId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Approval result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"autoResumed":{"type":"boolean"},"replayed":{"type":"boolean"}},"required":["success","autoResumed","replayed"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Only clinic members can approve reports"},"404":{"description":"Order or review not found"},"409":{"description":"Review or command conflict"}}}},"/orders/{id}/surgical-report/{reviewId}/request-changes":{"post":{"operationId":"requestSurgicalReportChanges","tags":["Orders"],"summary":"Request a new surgical report revision (clinic only)","description":"Keeps the approval task and hold open while requiring the lab to submit a new immutable revision.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"reviewId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","format":"uuid"},"notes":{"type":"string","minLength":1,"maxLength":2000}},"required":["commandId","notes"],"additionalProperties":false}}}},"responses":{"200":{"description":"Changes requested","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"replayed":{"type":"boolean"}},"required":["success","replayed"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Only clinic members can request changes"},"404":{"description":"Order or review not found"},"409":{"description":"Review or command conflict"}}}},"/orders/{id}/files":{"get":{"operationId":"getOrderFiles","tags":["Orders"],"summary":"Get order files","description":"Returns all files attached to an order along with product file requirements. Requirements define what file types each product expects (scans, photos, prescriptions, etc.) and their upload rules.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of order files and product file requirements","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"},"thumbnailUrl":{"type":["string","null"]},"fileRequirementId":{"type":["string","null"]},"orderPhaseId":{"type":["string","null"]},"uploadedBy":{"type":["string","null"]},"uploaderName":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orderId","name","type","mimeType","size","url","thumbnailUrl","fileRequirementId","orderPhaseId","uploadedBy","uploaderName","createdAt","updatedAt"]}},"requirements":{"type":"array","items":{}}},"required":["files","requirements"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"},"500":{"description":"Submitted order catalog revision is invalid"}}}},"/orders/{id}/components":{"get":{"operationId":"getOrderComponents","tags":["Orders"],"summary":"Get order components","description":"Returns all components (e.g., implant components, materials with lot numbers) attached to an order. Accessible by both labs and clinics that have access to the order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of order components with lot numbers and catalog references","content":{"application/json":{"schema":{"type":"object","properties":{"components":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"labComponentId":{"type":["string","null"]},"itemName":{"type":"string"},"lotNumber":{"type":"string"},"notes":{"type":["string","null"]},"addedBy":{"type":"string"},"addedByName":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orderId","labComponentId","itemName","lotNumber","notes","addedBy","addedByName","createdAt","updatedAt"]}}},"required":["components"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"403":{"description":"Access denied - organization does not have access to this order"},"404":{"description":"Order not found"}}},"post":{"operationId":"addOrderComponent","tags":["Orders"],"summary":"Add component to order","description":"Adds a component to an order with item name, lot number, and optional reference to the lab's component catalog. Only labs can add components to their orders.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lotNumber":{"type":"string","minLength":1,"maxLength":200},"itemName":{"type":"string","minLength":1,"maxLength":200},"labComponentId":{"type":"string"},"notes":{"type":"string","maxLength":500}},"required":["lotNumber","itemName"]}}}},"responses":{"201":{"description":"Created component record with user details","content":{"application/json":{"schema":{"type":"object","properties":{"component":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"labComponentId":{"type":["string","null"]},"itemName":{"type":"string"},"lotNumber":{"type":"string"},"notes":{"type":["string","null"]},"addedBy":{"type":"string"},"addedByName":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orderId","labComponentId","itemName","lotNumber","notes","addedBy","addedByName","createdAt","updatedAt"]}},"required":["component"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can add components to orders"},"404":{"description":"Order or catalog component not found"},"409":{"description":"Order is closed, unavailable, or payment-fenced"}}},"delete":{"operationId":"removeOrderComponent","tags":["Orders"],"summary":"Remove component from order","description":"Removes a component from an order. Only labs can remove components from their orders. The component ID is passed as a query parameter.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Component ID to remove","example":"7c9e6679-7425-40de-944b-e07fc1f90ae7"},"required":true,"description":"Component ID to remove","name":"componentId","in":"query"}],"responses":{"200":{"description":"Confirmation that the component was removed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Order ID and component ID are required"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can remove components from orders"},"404":{"description":"Order or component not found"},"409":{"description":"Order is closed, unavailable, or payment-fenced"}}}},"/orders/{id}/items":{"post":{"operationId":"addOrderItem","tags":["Orders"],"summary":"Add line item to an existing order","description":"Adds a new line item to a draft order. Pricing is resolved from the lab's authoritative price-list publication and catalog at insertion time.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productId":{"type":"string","description":"Product ID to add as a line item","example":"550e8400-e29b-41d4-a716-446655440000"},"quantity":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"toothGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","enum":["single","bridge"]},"teeth":{"type":"array","items":{"type":"object","properties":{"fdi":{"type":"integer"},"role":{"type":"string","enum":["crown","pontic"]},"support":{"type":"string","enum":["natural","implant"]},"implantSystemId":{"type":"string"},"implantPlatform":{"type":"string","maxLength":50}},"required":["fdi","role"]},"minItems":1}},"required":["id","type","teeth"]}},"materialId":{"type":"string"},"material":{"type":"string"},"shadeSystemId":{"type":"string"},"shadeOcclusal":{"type":"string"},"shadeMiddle":{"type":"string"},"shadeGingival":{"type":"string"},"shadeNotes":{"type":"string"},"defaultImplantSystemId":{"type":"string"},"defaultImplantPlatform":{"type":"string","maxLength":50},"notes":{"type":"string"},"customFieldValues":{"type":"object","additionalProperties":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"fileIds":{"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false}}},"required":["productId"]}}}},"responses":{"201":{"description":"Created order item","content":{"application/json":{"schema":{"type":"object","properties":{"item":{}}}}}},"400":{"description":"Validation failed"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can add items, or order is in a closed state"},"404":{"description":"Order or product not found"},"409":{"description":"Canonical product revision unavailable"}}}},"/orders/{id}/items/{itemId}":{"patch":{"operationId":"updateOrderItem","tags":["Orders"],"summary":"Update a line item on an existing order","description":"Updates the configuration of an existing draft line item and re-resolves authoritative pricing. Price-affecting fields are frozen after submission.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Order item ID","example":"660e8400-e29b-41d4-a716-446655440111"},"required":true,"description":"Order item ID","name":"itemId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"quantity":{"type":"integer","exclusiveMinimum":0},"toothGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","enum":["single","bridge"]},"teeth":{"type":"array","items":{"type":"object","properties":{"fdi":{"type":"integer"},"role":{"type":"string","enum":["crown","pontic"]},"support":{"type":"string","enum":["natural","implant"]},"implantSystemId":{"type":"string"},"implantPlatform":{"type":"string","maxLength":50}},"required":["fdi","role"]},"minItems":1}},"required":["id","type","teeth"]}},"materialId":{"type":"string"},"material":{"type":"string"},"shadeSystemId":{"type":"string"},"shadeOcclusal":{"type":"string"},"shadeMiddle":{"type":"string"},"shadeGingival":{"type":"string"},"shadeNotes":{"type":"string"},"defaultImplantSystemId":{"type":"string"},"defaultImplantPlatform":{"type":"string","maxLength":50},"notes":{"type":"string"},"customFieldValues":{"type":"object","additionalProperties":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"fileIds":{"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false}}}}}}},"responses":{"200":{"description":"Updated order item","content":{"application/json":{"schema":{"type":"object","properties":{"item":{}}}}}},"400":{"description":"Validation failed"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can edit items, or order is in a closed state"},"404":{"description":"Order or item not found"},"409":{"description":"Canonical product revision unavailable"}}},"delete":{"operationId":"deleteOrderItem","tags":["Orders"],"summary":"Remove a line item from an existing order","description":"Removes a line item from a draft order. Order items are frozen after submission.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Order item ID","example":"660e8400-e29b-41d4-a716-446655440111"},"required":true,"description":"Order item ID","name":"itemId","in":"path"}],"responses":{"200":{"description":"Item removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can remove items, or order is in a closed state"},"404":{"description":"Order or item not found"},"409":{"description":"Bundle members must be removed as a whole bundle"}}}},"/orders/{id}/messages":{"get":{"operationId":"getOrderMessages","tags":["Orders"],"summary":"Get order messages","description":"Returns messages for an order, filterable by type (all, notes, replies). Internal messages are visible only to the organization that authored them. Results are ordered by newest first.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Message type filter: all, notes, replies","example":"all"},"required":false,"description":"Message type filter: all, notes, replies","name":"type","in":"query"}],"responses":{"200":{"description":"List of order messages with sender details","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"senderUserId":{"type":"string"},"senderOrgId":{"type":"string"},"senderName":{"type":["string","null"]},"senderOrgName":{"type":["string","null"]},"content":{"type":"string"},"isInternal":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orderId","senderUserId","senderOrgId","senderName","senderOrgName","content","isInternal","createdAt","updatedAt"]}}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"}}}},"/orders/{id}/print-data":{"get":{"operationId":"getOrderPrintData","tags":["Orders"],"summary":"Get order print data","description":"Returns comprehensive order data formatted for document generation and printing, including lab/clinic addresses, line items with custom fields, patient details, and document settings (title, terms, footer). Patient data is masked for labs when hidePatientFromLab is enabled.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Complete print-ready order data with lab, clinic, items, and document settings","content":{"application/json":{"schema":{"type":"object","properties":{"order":{},"lab":{},"clinic":{},"clinicLocation":{},"items":{"type":"array","items":{}},"documentSettings":{}},"required":["items"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"},"500":{"description":"Submitted order catalog revision is invalid"}}}},"/orders/{id}/subscribe":{"get":{"operationId":"getOrderSubscription","tags":["Orders"],"summary":"Get subscription status","description":"Checks whether the authenticated user is subscribed to notifications for a specific order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Subscription status boolean","content":{"application/json":{"schema":{"type":"object","properties":{"subscribed":{"type":"boolean"}},"required":["subscribed"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"}}},"post":{"operationId":"subscribeToOrder","tags":["Orders"],"summary":"Subscribe to order","description":"Subscribes the authenticated user to receive notifications for a specific order. The user must belong to an organization (lab or clinic) that has access to the order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Confirmation that the user is now subscribed","content":{"application/json":{"schema":{"type":"object","properties":{"subscribed":{"type":"boolean"}},"required":["subscribed"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"404":{"description":"Order not found or not accessible by the current organization"}}},"delete":{"operationId":"unsubscribeFromOrder","tags":["Orders"],"summary":"Unsubscribe from order","description":"Unsubscribes the authenticated user from notifications for a specific order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Confirmation that the user is now unsubscribed","content":{"application/json":{"schema":{"type":"object","properties":{"subscribed":{"type":"boolean"}},"required":["subscribed"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"}}}},"/orders/{id}/tags":{"get":{"operationId":"getOrderTags","tags":["Orders"],"summary":"Get order tags","description":"Returns all tags applied to an order. Accessible by both labs and clinics that have access to the order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of tags applied to the order with tag details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"tagId":{"type":"string"},"appliedBy":{"type":"string"},"createdAt":{"type":"string"},"tag":{}},"required":["id","orderId","tagId","appliedBy","createdAt"]}}},"required":["data"]}}}},"400":{"description":"Order ID is required"},"401":{"description":"Unauthorized"},"403":{"description":"Access denied - organization does not have access to this order"},"404":{"description":"Order not found"}}},"post":{"operationId":"addOrderTag","tags":["Orders"],"summary":"Add tag to order","description":"Applies an existing tag from the lab's tag catalog to an order. The tag must be active and belong to the order's lab. Prevents duplicate tag applications.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tagId":{"type":"string","minLength":1}},"required":["tagId"]}}}},"responses":{"201":{"description":"Created tag application with full tag details","content":{"application/json":{"schema":{"type":"object","properties":{"orderTag":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"tagId":{"type":"string"},"appliedBy":{"type":"string"},"createdAt":{"type":"string"},"tag":{}},"required":["id","orderId","tagId","appliedBy","createdAt"]}},"required":["orderTag"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Access denied - organization does not have access to this order"},"404":{"description":"Order or tag not found"},"409":{"description":"Tag is already applied to this order"}}},"delete":{"operationId":"removeOrderTag","tags":["Orders"],"summary":"Remove tag from order","description":"Removes a tag from an order. The tag ID is passed as a query parameter. Accessible by both labs and clinics with access to the order.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Tag ID to remove","example":"7c9e6679-7425-40de-944b-e07fc1f90ae7"},"required":true,"description":"Tag ID to remove","name":"tagId","in":"query"}],"responses":{"200":{"description":"Confirmation that the tag was removed from the order","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Order ID and tag ID are required"},"401":{"description":"Unauthorized"},"403":{"description":"Access denied - organization does not have access to this order"},"404":{"description":"Order not found or tag not applied to this order"}}}},"/shipments":{"post":{"operationId":"createShipment","tags":["Shipments"],"summary":"Create a quantity-level outbound shipment","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"partnershipId":{"type":"string","minLength":1},"method":{"type":"string","enum":["carrier","local_driver"]},"destination":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"addressLine1":{"type":"string","minLength":1,"maxLength":250},"addressLine2":{"type":"string","maxLength":250},"city":{"type":"string","minLength":1,"maxLength":120},"region":{"type":"string","maxLength":120},"postcode":{"type":"string","minLength":1,"maxLength":30},"country":{"type":"string"},"contactName":{"type":"string","maxLength":160},"contactPhone":{"type":"string","maxLength":50}},"required":["name","addressLine1","city","postcode","country"],"additionalProperties":false},"destinationLocationId":{"type":"string","minLength":1},"driverName":{"type":"string","maxLength":160},"driverPhone":{"type":"string","maxLength":50},"scheduledDate":{"type":"string"},"notes":{"type":"string","maxLength":5000},"items":{"type":"array","items":{"type":"object","properties":{"clientReference":{"type":"string","minLength":1,"maxLength":100},"orderItemId":{"type":"string","minLength":1},"quantity":{"type":"integer","exclusiveMinimum":0,"maximum":100000},"kind":{"type":"string","enum":["fulfillment","replacement"],"default":"fulfillment"},"replacementForShipmentItemId":{"type":"string","minLength":1},"notes":{"type":"string","maxLength":2000}},"required":["clientReference","orderItemId","quantity"],"additionalProperties":false},"minItems":1,"maxItems":500},"packages":{"type":"array","items":{"type":"object","properties":{"clientReference":{"type":"string","minLength":1,"maxLength":100},"weightGrams":{"type":"integer","exclusiveMinimum":0},"lengthMm":{"type":"integer","exclusiveMinimum":0},"widthMm":{"type":"integer","exclusiveMinimum":0},"heightMm":{"type":"integer","exclusiveMinimum":0},"items":{"type":"array","items":{"type":"object","properties":{"allocationReference":{"type":"string","minLength":1,"maxLength":100},"quantity":{"type":"integer","exclusiveMinimum":0,"maximum":100000}},"required":["allocationReference","quantity"],"additionalProperties":false},"minItems":1,"maxItems":500}},"required":["clientReference","items"],"additionalProperties":false},"minItems":1,"maxItems":100}},"required":["commandId","partnershipId","method","items"],"additionalProperties":false}}}},"responses":{"201":{"description":"Shipment created"},"400":{"description":"Invalid shipment allocation"},"401":{"description":"Unauthorized"},"403":{"description":"Lab access required"},"409":{"description":"Command identity or allocation conflict"}}},"get":{"operationId":"listShipments","tags":["Shipments"],"summary":"List outbound shipments visible to the active organization","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","enum":["draft","packing","ready","in_transit","delivery_exception","delivered","returned","cancelled"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"partnershipId","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated shipments"},"401":{"description":"Unauthorized"}}}},"/shipments/{id}/transition":{"post":{"operationId":"transitionShipment","tags":["Shipments"],"summary":"Transition an outbound shipment","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"toStatus":{"type":"string","enum":["draft","packing","ready","in_transit","delivery_exception","delivered","returned","cancelled"]},"expectedVersion":{"type":"integer","exclusiveMinimum":0},"occurredAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"details":{"type":"object","additionalProperties":{}},"deliveryProof":{"type":"object","properties":{"recipientName":{"type":"string","minLength":1,"maxLength":160},"proofType":{"type":"string","enum":["signature","photo","manual"]},"fileId":{"type":"string","minLength":1}},"required":["recipientName","proofType"],"additionalProperties":false}},"required":["commandId","toStatus","expectedVersion"],"additionalProperties":false}}}},"responses":{"200":{"description":"Shipment transitioned"},"400":{"description":"Invalid transition"},"404":{"description":"Shipment not found"},"409":{"description":"Stale version or command identity conflict"}}}},"/shipments/{id}/labels/manual":{"post":{"operationId":"createManualShipmentLabel","tags":["Shipments"],"summary":"Attach a manually purchased carrier label","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedVersion":{"type":"integer","exclusiveMinimum":0},"packageId":{"type":"string","minLength":1},"carrier":{"type":"string","minLength":1,"maxLength":120},"serviceCode":{"type":"string","maxLength":120},"trackingNumber":{"type":"string","minLength":1,"maxLength":200},"trackingUrl":{"type":"string","maxLength":2000,"format":"uri"},"cost":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]}},"required":["commandId","expectedVersion","packageId","carrier","trackingNumber","currency"],"additionalProperties":false}}}},"responses":{"200":{"description":"Manual label attached"},"404":{"description":"Shipment or package not found"},"409":{"description":"Stale shipment or command identity conflict"}}}},"/shipments/{id}/labels/{labelId}/void":{"post":{"operationId":"voidShipmentLabel","tags":["Shipments"],"summary":"Void an attached manual carrier label","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"labelId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedVersion":{"type":"integer","exclusiveMinimum":0},"reason":{"type":"string","minLength":1,"maxLength":500}},"required":["commandId","expectedVersion"],"additionalProperties":false}}}},"responses":{"200":{"description":"Label voided"},"404":{"description":"Shipment or label not found"},"409":{"description":"Stale shipment or provider void required"}}}},"/shipments/{id}/scans":{"post":{"operationId":"recordShipmentScan","tags":["Shipments"],"summary":"Record a replay-safe package pack/unpack scan","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedVersion":{"type":"integer","exclusiveMinimum":0},"packageId":{"type":"string","minLength":1},"shipmentItemId":{"type":"string","minLength":1},"action":{"type":"string","enum":["pack","unpack"]},"quantity":{"type":"integer","exclusiveMinimum":0,"maximum":100000},"barcode":{"type":"string","maxLength":500}},"required":["commandId","expectedVersion","packageId","shipmentItemId","action","quantity"],"additionalProperties":false}}}},"responses":{"200":{"description":"Scan recorded"},"400":{"description":"Scan violates the package allocation"},"404":{"description":"Shipment or package allocation not found"},"409":{"description":"Stale shipment or command identity conflict"}}}},"/shipments/{id}":{"get":{"operationId":"getShipment","tags":["Shipments"],"summary":"Get an outbound shipment","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Shipment detail"},"404":{"description":"Shipment not found"}}}},"/shipments/{id}/rates":{"post":{"operationId":"quoteShipmentPackageRates","tags":["Shipments"],"summary":"Quote bounded ShipStation rates for one shipment package","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"packageId":{"type":"string","minLength":1},"carrierIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"minItems":1,"maxItems":20}},"required":["packageId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Signed, short-lived rate choices"},"400":{"description":"Shipment address or package data is incomplete"},"404":{"description":"Shipment package not found"},"409":{"description":"Shipment or shipping integration is not eligible"},"429":{"description":"Provider operation budget exhausted"},"502":{"description":"Rate provider failed safely"},"503":{"description":"Provider integration disabled"}}}},"/shipments/{id}/labels/provider":{"post":{"operationId":"purchaseProviderShipmentLabel","tags":["Shipments"],"summary":"Purchase a ShipStation label from a signed rate","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedVersion":{"type":"integer","exclusiveMinimum":0},"rateToken":{"type":"string","minLength":64,"maxLength":4096}},"required":["commandId","expectedVersion","rateToken"],"additionalProperties":false}}}},"responses":{"200":{"description":"Existing label command replayed"},"201":{"description":"Provider label purchased and stored"},"202":{"description":"Provider outcome is being recovered or finalized"},"404":{"description":"Shipment package not found"},"409":{"description":"Stale or conflicting shipment command"},"429":{"description":"Paid provider budget exhausted"},"503":{"description":"Provider integration disabled"}}}},"/shipments/{id}/labels/{labelId}/provider-void":{"post":{"operationId":"voidProviderShipmentLabel","tags":["Shipments"],"summary":"Void a ShipStation label without repeating the provider mutation","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"labelId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedVersion":{"type":"integer","exclusiveMinimum":0},"reason":{"type":"string","minLength":1,"maxLength":500}},"required":["commandId","expectedVersion"],"additionalProperties":false}}}},"responses":{"200":{"description":"Existing void command replayed"},"201":{"description":"Provider label voided"},"202":{"description":"Provider outcome is being recovered or finalized"},"404":{"description":"Provider label not found"},"409":{"description":"Stale or conflicting shipment command"},"429":{"description":"Provider mutation budget exhausted"},"503":{"description":"Provider integration disabled"}}}},"/shipments/{id}/provider-operations/{operationId}":{"get":{"operationId":"getShipmentProviderOperation","tags":["Shipments"],"summary":"Get durable provider operation status","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"operationId","in":"path"}],"responses":{"200":{"description":"Provider operation status"},"404":{"description":"Provider operation not found"}}}},"/shipments/{id}/labels/{labelId}/document":{"get":{"operationId":"downloadShipmentLabelDocument","tags":["Shipments"],"summary":"Download an authorized immutable carrier-label PDF","security":[{"cookieAuth":[]},{"bearerAuth":[]},{"organizationApiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"labelId","in":"path"}],"responses":{"200":{"description":"Carrier-label PDF"},"404":{"description":"Stored provider label not found"},"413":{"description":"Stored provider label exceeds its fixed limit"},"429":{"description":"Download budget exhausted"}}}},"/order-tags":{"get":{"tags":["Order Tags"],"operationId":"listOrderTags","summary":"List order tags","description":"Returns a paginated list of order tags for the organization. Labs see their own tags; clinics must provide a labId to see a partner lab's tags.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Include inactive tags","example":"false"},"required":false,"description":"Include inactive tags","name":"includeInactive","in":"query"},{"schema":{"type":"string","description":"Search term","example":"search term"},"required":false,"description":"Search term","name":"search","in":"query"},{"schema":{"type":"string","description":"Page number","example":"1"},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"string","description":"Items per page","example":"20"},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","description":"Lab ID (required for clinic users)","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Lab ID (required for clinic users)","name":"labId","in":"query"}],"responses":{"200":{"description":"Paginated list of order tags","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters or missing labId for clinic"},"401":{"description":"Unauthorized — valid session required"}}},"post":{"tags":["Order Tags"],"operationId":"createOrderTag","summary":"Create an order tag","description":"Creates a new order tag with a name, color, and optional description. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":50},"color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","default":"#6366f1"},"description":{"type":"string","maxLength":200},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Tag created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"tag":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — only lab organizations can create tags"}}}},"/order-tags/{id}":{"get":{"tags":["Order Tags"],"operationId":"getOrderTag","summary":"Get an order tag","description":"Returns a single order tag by ID. Clinics can only view tags from their partner labs.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Tag ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Tag ID","name":"id","in":"path"}],"responses":{"200":{"description":"Tag details","content":{"application/json":{"schema":{"type":"object","properties":{"tag":{}}}}}},"400":{"description":"Tag ID is required"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Tag not found or no partnership access"}}},"put":{"tags":["Order Tags"],"operationId":"updateOrderTag","summary":"Update an order tag","description":"Updates an existing order tag's name, color, description, active status, or sort order. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Tag ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Tag ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":50},"color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","default":"#6366f1"},"description":{"type":"string","maxLength":200},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Tag updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"tag":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — only lab organizations can update tags"},"404":{"description":"Tag not found"}}},"delete":{"tags":["Order Tags"],"operationId":"deleteOrderTag","summary":"Deactivate an order tag","description":"Soft-deletes an order tag by setting it to inactive. The tag remains in the database for historical reference. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Tag ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Tag ID","name":"id","in":"path"}],"responses":{"200":{"description":"Tag deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"tag":{},"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Tag ID is required"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — only lab organizations can delete tags"},"404":{"description":"Tag not found"}}}},"/order-filter-presets":{"get":{"tags":["Order Filter Presets"],"operationId":"listOrderFilterPresets","summary":"List order filter presets","description":"Returns the organization's persisted order filter presets, ordered by display position. Defaults are initialized during organization creation or by the explicit settings-defaults command.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Include inactive presets","example":"false"},"required":false,"description":"Include inactive presets","name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of order filter presets","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}}},"required":["data"]}}}},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Preset capacity exceeded"}}},"post":{"tags":["Order Filter Presets"],"operationId":"createOrderFilterPreset","summary":"Create an order filter preset","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"icon":{"type":["string","null"],"maxLength":60},"filters":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"types":{"type":"array","items":{"type":"string","enum":["digital","physical"]}},"doctorName":{"type":["string","null"]},"patientId":{"type":["string","null"]},"labId":{"type":["string","null"]},"hasFiles":{"type":["boolean","null"]},"hasAdvanceRequest":{"type":["boolean","null"]}},"default":{},"additionalProperties":false},"sort":{"type":"object","properties":{"field":{"type":"string","enum":["createdAt","dueDate","submittedAt","orderNumber","status"],"default":"createdAt"},"direction":{"type":"string","enum":["asc","desc"],"default":"desc"}},"default":{"field":"createdAt","direction":"desc"},"additionalProperties":false},"columns":{"type":"array","items":{"type":"string","minLength":1},"default":[]},"layout":{"type":"string","enum":["table"],"default":"table"},"isDefault":{"type":"boolean","default":false}},"required":["name"]}}}},"responses":{"201":{"description":"Preset created","content":{"application/json":{"schema":{"type":"object","properties":{"preset":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Preset capacity exceeded"}}}},"/order-filter-presets/counts":{"get":{"tags":["Order Filter Presets"],"operationId":"getOrderFilterPresetCounts","summary":"Count orders per filter preset","description":"Returns the number of orders matching each active preset's filter, keyed by preset id. Powers the stat cards on the orders page.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Counts keyed by preset id","content":{"application/json":{"schema":{"type":"object","properties":{"counts":{"type":"object","additionalProperties":{"type":"number"}}},"required":["counts"]}}}},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Preset capacity exceeded"}}}},"/order-filter-presets/reorder":{"post":{"tags":["Order Filter Presets"],"operationId":"reorderOrderFilterPresets","summary":"Reorder order filter presets","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"presetIds":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["presetIds"]}}}},"responses":{"200":{"description":"Presets reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"One or more presets not found"}}}},"/order-filter-presets/{id}":{"put":{"tags":["Order Filter Presets"],"operationId":"updateOrderFilterPreset","summary":"Update an order filter preset","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Filter preset ID","example":"uuid"},"required":true,"description":"Filter preset ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"icon":{"type":["string","null"],"maxLength":60},"filters":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"types":{"type":"array","items":{"type":"string","enum":["digital","physical"]}},"doctorName":{"type":["string","null"]},"patientId":{"type":["string","null"]},"labId":{"type":["string","null"]},"hasFiles":{"type":["boolean","null"]},"hasAdvanceRequest":{"type":["boolean","null"]}},"default":{},"additionalProperties":false},"sort":{"type":"object","properties":{"field":{"type":"string","enum":["createdAt","dueDate","submittedAt","orderNumber","status"],"default":"createdAt"},"direction":{"type":"string","enum":["asc","desc"],"default":"desc"}},"default":{"field":"createdAt","direction":"desc"},"additionalProperties":false},"columns":{"type":"array","items":{"type":"string","minLength":1},"default":[]},"layout":{"type":"string","enum":["table"],"default":"table"},"isDefault":{"type":"boolean","default":false}}}}}},"responses":{"200":{"description":"Preset updated","content":{"application/json":{"schema":{"type":"object","properties":{"preset":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Preset not found"}}},"delete":{"tags":["Order Filter Presets"],"operationId":"deleteOrderFilterPreset","summary":"Delete an order filter preset","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Filter preset ID","example":"uuid"},"required":true,"description":"Filter preset ID","name":"id","in":"path"}],"responses":{"200":{"description":"Preset deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Preset not found"}}}},"/patients":{"get":{"tags":["Patients"],"operationId":"searchPatients","summary":"Search patients (autocomplete / combobox)","description":"Search patients with fuzzy matching for autocomplete/combobox use. Labs see patients from partnered clinics (respecting share-all-patients and lab access settings). Clinics see only their own patients. Anonymized patients are masked for labs.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","default":"","description":"Search query for patient name, code, or external ID","example":"John"},"required":false,"description":"Search query for patient name, code, or external ID","name":"q","in":"query"},{"schema":{"type":"string","description":"Filter patients by clinic ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter patients by clinic ID","name":"clinicId","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50,"description":"Maximum number of results to return","example":50},"required":false,"description":"Maximum number of results to return","name":"limit","in":"query"}],"responses":{"200":{"description":"List of matching patients","content":{"application/json":{"schema":{"type":"object","properties":{"patients":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clinicId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"gender":{"type":["string","null"]},"dateOfBirth":{"type":["string","null"]},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"externalId":{"type":["string","null"]},"clinicName":{"type":["string","null"]},"patientCode":{"type":["string","null"]},"fullName":{"type":"string"},"clinic":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"isAnonymized":{"type":"boolean"}},"required":["id","clinicId","firstName","lastName","gender","dateOfBirth","phone","email","externalId","clinicName","patientCode","fullName","clinic","isAnonymized"]}}},"required":["patients"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"403":{"description":"Access denied to clinic's patients"},"500":{"description":"Internal server error"}}},"post":{"tags":["Patients"],"operationId":"createPatient","summary":"Create a new patient","description":"Create a new patient record. Clinics can only create patients for themselves. Labs can create patients for partnered clinics and automatically get lab access granted. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"clinicId":{"type":"string","minLength":1},"firstName":{"type":"string","minLength":1},"lastName":{"type":"string","minLength":1},"gender":{"type":"string","enum":["male","female","other","prefer_not_to_say"]},"dateOfBirth":{"type":["string","null"]},"phone":{"type":"string"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"externalId":{"type":"string"},"notes":{"type":"string"}},"required":["clinicId","firstName","lastName"]}}}},"responses":{"201":{"description":"Patient created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"patient":{"type":"object","properties":{"id":{"type":"string"},"clinicId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"fullName":{"type":"string"}},"required":["id","clinicId","firstName","lastName","fullName"],"additionalProperties":{}}},"required":["patient"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Cannot create patient for this clinic"},"409":{"description":"A patient with this external ID already exists for this clinic"},"500":{"description":"Internal server error"}}}},"/patients/list":{"get":{"tags":["Patients"],"operationId":"listPatients","summary":"Paginated patient list","description":"Retrieve a paginated list of patients with order counts. Labs see patients from partnered clinics (respecting share-all-patients and lab access settings). Clinics see only their own. Supports search, sorting, and clinic filtering.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"description":"Page number","example":1},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","description":"Search term for patient name or external ID","example":"John"},"required":false,"description":"Search term for patient name or external ID","name":"search","in":"query"},{"schema":{"type":"string","description":"Filter by clinic ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by clinic ID","name":"clinicId","in":"query"},{"schema":{"type":"string","enum":["name","practice","dateOfBirth","orders","createdAt"],"default":"name","description":"Field to sort by","example":"name"},"required":false,"description":"Field to sort by","name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc","description":"Sort direction","example":"asc"},"required":false,"description":"Sort direction","name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of patients with order counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"fullName":{"type":"string"},"dateOfBirth":{"type":["string","null"]},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"externalId":{"type":["string","null"]},"clinicId":{"type":"string"},"clinicName":{"type":["string","null"]},"orderCount":{"type":"number"},"createdAt":{"type":"string"},"patientCode":{"type":["string","null"]},"isAnonymized":{"type":"boolean"}},"required":["id","firstName","lastName","fullName","dateOfBirth","phone","email","externalId","clinicId","clinicName","orderCount","createdAt","patientCode","isAnonymized"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/patients/{id}":{"get":{"tags":["Patients"],"operationId":"getPatient","summary":"Get a single patient by ID","description":"Retrieve a patient's details. Labs can access patients from partnered clinics (subject to lab access and share-all-patients settings). Anonymized patients return masked data for labs. Clinics can only access their own patients.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Patient ID","name":"id","in":"path"}],"responses":{"200":{"description":"Patient details (may be anonymized for labs)","content":{"application/json":{"schema":{"type":"object","properties":{"patient":{"type":"object","properties":{"id":{"type":"string"},"clinicId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"gender":{"type":["string","null"]},"dateOfBirth":{"type":["string","null"]},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"externalId":{"type":["string","null"]},"notes":{"type":["string","null"]},"clinicName":{"type":["string","null"]},"fullName":{"type":"string"},"patientCode":{"type":["string","null"]},"isAnonymized":{"type":"boolean"}},"required":["id","clinicId","firstName","lastName","gender","dateOfBirth","phone","email","externalId","notes","clinicName","fullName","patientCode","isAnonymized"]}},"required":["patient"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Access denied to this patient"},"404":{"description":"Patient not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Patients"],"operationId":"updatePatient","summary":"Update a patient's details","description":"Update an existing patient's details. Clinics can only update their own patients. Labs can update patients from partnered clinics they have access to (subject to lab access and share-all-patients settings). Anonymized/hidden patients cannot be edited. The patient's clinic and patient code cannot be changed.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Patient ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"firstName":{"type":"string","minLength":1},"lastName":{"type":"string","minLength":1},"gender":{"type":"string","enum":["male","female","other","prefer_not_to_say"]},"dateOfBirth":{"type":["string","null"]},"phone":{"type":"string"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"externalId":{"type":"string"},"notes":{"type":"string"}},"required":["firstName","lastName"]}}}},"responses":{"200":{"description":"Patient updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"patient":{"type":"object","properties":{"id":{"type":"string"},"clinicId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"fullName":{"type":"string"}},"required":["id","clinicId","firstName","lastName","fullName"],"additionalProperties":{}}},"required":["patient"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Access denied to this patient"},"404":{"description":"Patient not found"},"409":{"description":"A patient with this external ID already exists for this clinic"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Patients"],"operationId":"erasePatientIdentity","summary":"Erase a patient identity","description":"Immediately pseudonymizes a clinic-owned patient and starts bounded cleanup of retained identity copies. Replays are idempotent.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Identity erasure accepted or already in progress","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["requested","already_requested"]}},"required":["status"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Clinic manager access required"},"404":{"description":"Patient not found"},"409":{"description":"Controller retention hold blocks identity erasure"},"500":{"description":"Internal server error"}}}},"/patients/{id}/lab-access":{"get":{"tags":["Patients"],"operationId":"listPatientLabAccess","summary":"List labs with access to a patient","description":"List labs with effective access through either an explicit live grant or the active partnership's share-all-patients policy. Clinic only - the patient must belong to the current clinic.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Patient ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of labs with effective access and its source","content":{"application/json":{"schema":{"type":"object","properties":{"labs":{"type":"array","items":{"type":"object","properties":{"labId":{"type":"string"},"labName":{"type":["string","null"]},"grantedAt":{},"accessSource":{"type":"string","enum":["explicit","partnership"]},"grantSource":{"type":["string","null"],"enum":["manual","order","patient_creation",null]},"sourceOrderId":{"type":["string","null"]}},"required":["labId","labName","accessSource","grantSource","sourceOrderId"]}}},"required":["labs"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - clinic only"},"404":{"description":"Patient not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Patients"],"operationId":"grantPatientLabAccess","summary":"Grant lab access to a patient","description":"Grant a specific lab explicit access to a patient record. The lab must have an active partnership with the clinic. Clinic only - the patient must belong to the current clinic. Idempotent - granting access twice is a no-op.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Patient ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1}},"required":["labId"]}}}},"responses":{"201":{"description":"Lab access granted to patient","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"No active partnership with this lab"},"404":{"description":"Patient not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Patients"],"operationId":"revokePatientLabAccess","summary":"Revoke lab access to a patient","description":"Revoke a lab's effective access while retaining grant and revocation audit evidence. Under partnership-wide sharing this creates a per-patient exclusion. Active and suspended partnership epochs are supported. Clinic only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Patient ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Patient ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1}},"required":["labId"]}}}},"responses":{"200":{"description":"Lab access revoked from patient","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - clinic only"},"404":{"description":"Patient not found"},"500":{"description":"Internal server error"}}}},"/partnerships":{"get":{"tags":["Partnerships"],"operationId":"listPartnerships","summary":"List partnerships for the current organization","description":"Retrieve a paginated list of partnerships with order statistics. Labs see clinic partners, clinics see lab partners. Supports search, status filtering, practice group filtering, and configurable sorting. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"description":"Page number","example":1},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","description":"Search term for partner name, email, or city","example":"London"},"required":false,"description":"Search term for partner name, email, or city","name":"search","in":"query"},{"schema":{"anyOf":[{"type":"string","enum":["pending","active","suspended","terminated","rejected"]},{"type":"string","enum":["all"]}],"default":"all","description":"Filter by partnership status","example":"active"},"required":false,"description":"Filter by partnership status","name":"status","in":"query"},{"schema":{"type":"string","description":"Filter by practice group ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Filter by practice group ID","name":"practiceGroupId","in":"query"},{"schema":{"type":"string","enum":["name","totalOrders","activeOrders","lastOrderDate","createdAt"],"default":"name","description":"Field to sort by","example":"name"},"required":false,"description":"Field to sort by","name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc","description":"Sort direction","example":"asc"},"required":false,"description":"Sort direction","name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of partnerships with order statistics","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"partnerId":{"type":"string"},"partnerName":{"type":["string","null"]},"partnerEmail":{"type":["string","null"]},"partnerPhone":{"type":["string","null"]},"partnerCity":{"type":["string","null"]},"partnerLogo":{"type":["string","null"]},"status":{"type":"string"},"initiatedBy":{"type":["string","null"]},"totalOrders":{"type":"number"},"activeOrders":{"type":"number"},"lastOrderDate":{"type":["string","null"]},"practiceGroupId":{"type":["string","null"]},"practiceGroupName":{"type":["string","null"]},"billingOwnerId":{"type":["string","null"]},"partnerProvisioningStatus":{"type":"string","enum":["claimed","pending_claim","managed","abandoned"]},"claimKind":{"type":["string","null"],"enum":["provisional_owner","managed_owner",null]},"createdAt":{"type":"string"}},"required":["id","partnerId","partnerName","partnerEmail","partnerPhone","partnerCity","partnerLogo","status","initiatedBy","totalOrders","activeOrders","lastOrderDate","practiceGroupId","practiceGroupName","billingOwnerId","partnerProvisioningStatus","claimKind","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/partnerships/search":{"get":{"tags":["Partnerships"],"operationId":"searchPartners","summary":"Search for potential partners","description":"Search for discoverable organizations to form new partnerships with. Excludes existing partners. Supports text search, organization type filtering, and geographic bounding box filtering. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Search query for organization name","example":"Dental"},"required":false,"description":"Search query for organization name","name":"q","in":"query"},{"schema":{"type":"string","enum":["lab","clinic"],"description":"Organization type to search for","example":"clinic"},"required":true,"description":"Organization type to search for","name":"type","in":"query"},{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Minimum latitude for geographic bounding box","example":51.4},"required":false,"description":"Minimum latitude for geographic bounding box","name":"minLat","in":"query"},{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Maximum latitude for geographic bounding box","example":51.6},"required":false,"description":"Maximum latitude for geographic bounding box","name":"maxLat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Minimum longitude for geographic bounding box","example":-0.2},"required":false,"description":"Minimum longitude for geographic bounding box","name":"minLng","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Maximum longitude for geographic bounding box","example":0.1},"required":false,"description":"Maximum longitude for geographic bounding box","name":"maxLng","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Maximum number of results to return","example":50},"required":false,"description":"Maximum number of results to return","name":"limit","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1,"description":"Page number","example":1},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"string","description":"Comma-separated list of service/category name fragments to filter by (lab search only). Each fragment is matched case-insensitively against the lab's product category names.","example":"crown,implant"},"required":false,"description":"Comma-separated list of service/category name fragments to filter by (lab search only). Each fragment is matched case-insensitively against the lab's product category names.","name":"categories","in":"query"},{"schema":{"type":"string","enum":["name","distance"],"default":"name","description":"Sort order. 'distance' requires clinicLat and clinicLng; falls back to 'name' otherwise.","example":"distance"},"required":false,"description":"Sort order. 'distance' requires clinicLat and clinicLng; falls back to 'name' otherwise.","name":"sortBy","in":"query"},{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Requesting organization's latitude (for distance sort).","example":51.5},"required":false,"description":"Requesting organization's latitude (for distance sort).","name":"clinicLat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Requesting organization's longitude (for distance sort).","example":-0.12},"required":false,"description":"Requesting organization's longitude (for distance sort).","name":"clinicLng","in":"query"}],"responses":{"200":{"description":"Paginated search results of potential partner organizations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"city":{"type":["string","null"]},"country":{"type":["string","null"]},"logo":{"type":["string","null"]},"latitude":{"type":["string","null"]},"longitude":{"type":["string","null"]},"description":{"type":["string","null"]},"hasCoverImage":{"type":"boolean"},"categories":{"type":"array","items":{"type":"string"}},"partnershipStatus":{"type":["string","null"]},"partnershipId":{"type":["string","null"]}},"required":["id","name","city","country","logo","latitude","longitude","description","hasCoverImage","categories","partnershipStatus","partnershipId"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/partnerships/invite":{"post":{"tags":["Partnerships"],"operationId":"inviteClinic","summary":"Invite a new clinic to GuideLab","description":"Create a new clinic organization and pending partnership in one step. Sends an invitation email to the clinic manager. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"clinicName":{"type":"string","minLength":1,"maxLength":200},"address":{"type":"string","maxLength":500},"city":{"type":"string","maxLength":100},"postcode":{"type":"string","maxLength":20},"country":{"type":"string"},"managerEmail":{"type":"string","format":"email"},"managerName":{"type":"string","minLength":1,"maxLength":200}},"required":["clinicName","managerEmail","managerName"]}}}},"responses":{"201":{"description":"Clinic organization created and partnership established","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{},"partnership":{}}}}}},"400":{"description":"Invalid request or clinic name already exists"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/invite-owner":{"post":{"tags":["Partnerships"],"operationId":"inviteManagedClinicOwner","summary":"Invite an owner to claim a lab-managed clinic","description":"Starts an ownership handoff for an imported clinic while preserving its existing active partnership and history.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"managerEmail":{"type":"string","format":"email"},"managerName":{"type":"string","minLength":1,"maxLength":200}},"required":["managerEmail"]}}}},"responses":{"201":{"description":"Ownership claim invitation created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"organizationId":{"type":"string"}},"required":["success","organizationId"]}}}},"400":{"description":"Invalid ownership handoff"},"401":{"description":"Unauthorized"},"403":{"description":"Lab manager required"},"404":{"description":"Managed clinic partnership not found"},"409":{"description":"Clinic state changed concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/claim/cancel":{"post":{"tags":["Partnerships"],"operationId":"cancelManagedClinicClaim","summary":"Cancel a managed clinic ownership handoff","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Ownership handoff canceled"},"401":{"description":"Unauthorized"},"403":{"description":"Lab manager required"},"404":{"description":"Pending managed claim not found"},"409":{"description":"Claim resolved concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/request":{"post":{"tags":["Partnerships"],"operationId":"requestPartnership","summary":"Request a partnership with another organization","description":"Send a partnership request to another organization. The target must be a different type (lab requests clinic, clinic requests lab). Sends a notification to the target organization's owners. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"targetOrgId":{"type":"string","minLength":1}},"required":["targetOrgId"]}}}},"responses":{"201":{"description":"Partnership request created with pending status","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{}}}}}},"400":{"description":"Invalid request or same organization type"},"401":{"description":"Unauthorized"},"404":{"description":"Target organization not found"},"409":{"description":"Partnership already exists between these organizations"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}":{"get":{"tags":["Partnerships"],"operationId":"getPartnership","summary":"Get partnership detail view","description":"Retrieve full partnership details including partner organization info, order/patient statistics, practice group, and gallery images. Only accessible by organizations that are part of the partnership.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full partnership detail with partner info and statistics","content":{"application/json":{"schema":{"type":"object","properties":{"partnershipId":{"type":"string"},"partnerId":{"type":"string"},"partnerType":{"type":"string","enum":["lab","clinic"]},"status":{"type":"string"},"initiatedBy":{"type":["string","null"]},"practiceGroupName":{"type":["string","null"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"},"partner":{"type":"object","properties":{"name":{"type":["string","null"]},"logo":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"billingEmail":{"type":["string","null"]},"address":{"type":["string","null"]},"addressLine2":{"type":["string","null"]},"city":{"type":["string","null"]},"postcode":{"type":["string","null"]},"country":{"type":["string","null"]},"website":{"type":["string","null"]},"companyNumber":{"type":["string","null"]},"taxRegistrationNumber":{"type":["string","null"]},"description":{"type":["string","null"]},"hasCoverImage":{"type":"boolean"},"latitude":{"type":["string","null"]},"longitude":{"type":["string","null"]}},"required":["name","logo","email","phone","billingEmail","address","addressLine2","city","postcode","country","website","companyNumber","taxRegistrationNumber","description","hasCoverImage","latitude","longitude"]},"stats":{"type":"object","properties":{"totalOrders":{"type":"number"},"activeOrders":{"type":"number"},"totalPatients":{"type":["number","null"]}},"required":["totalOrders","activeOrders","totalPatients"]},"gallery":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sortOrder":{"type":["number","null"]}},"required":["id","sortOrder"]}}},"required":["partnershipId","partnerId","partnerType","status","initiatedBy","practiceGroupName","notes","createdAt","partner","stats","gallery"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Partnership not found"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/detail":{"get":{"tags":["Partnerships"],"operationId":"getPartnershipClinicDetail","summary":"Get clinic detail view for a partnership","description":"Retrieve detailed clinic information for a partnership, including clinic contact info, order statistics, and patient count. Lab only - uses the partnership ID as the path parameter.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Clinic detail with contact info, order and patient stats","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"clinicId":{"type":"string"},"status":{"type":"string"},"practiceGroupName":{"type":["string","null"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"},"clinic":{"type":"object","properties":{"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"address":{"type":["string","null"]},"addressLine2":{"type":["string","null"]},"city":{"type":["string","null"]},"postcode":{"type":["string","null"]},"country":{"type":["string","null"]},"website":{"type":["string","null"]},"billingEmail":{"type":["string","null"]},"companyNumber":{"type":["string","null"]},"taxRegistrationNumber":{"type":["string","null"]}},"required":["name","email","phone","address","addressLine2","city","postcode","country","website","billingEmail","companyNumber","taxRegistrationNumber"]},"stats":{"type":"object","properties":{"totalOrders":{"type":"number"},"activeOrders":{"type":"number"},"totalPatients":{"type":"number"}},"required":["totalOrders","activeOrders","totalPatients"]}},"required":["id","clinicId","status","practiceGroupName","notes","createdAt","clinic","stats"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"409":{"description":"Partnership invitation was resolved concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/accept":{"post":{"tags":["Partnerships"],"operationId":"acceptPartnership","summary":"Accept a pending partnership request","description":"Accept a pending partnership request. Only the receiving party can accept (e.g., if a lab initiated, only the clinic can accept). Sends a notification to the initiating party. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Partnership accepted and activated","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{}}}}}},"400":{"description":"Partnership is not pending"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"409":{"description":"Partnership invitation was resolved concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/cancel":{"post":{"tags":["Partnerships"],"operationId":"cancelPartnership","summary":"Cancel a pending partnership request","description":"Cancel a pending partnership request. Only the initiating party can cancel. Also cancels any associated pending invitations. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Partnership request cancelled and deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"Partnership is not pending"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"409":{"description":"Partnership invitation was resolved concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/reject":{"post":{"tags":["Partnerships"],"operationId":"rejectPartnership","summary":"Reject a pending partnership request","description":"Reject a pending partnership request. Only the receiving party can reject. Sends a notification to the initiating party and retains the rejected relationship epoch as immutable history. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Partnership rejected and retained as history","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"Partnership is not pending"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"409":{"description":"Partnership status changed concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/reactivate":{"post":{"tags":["Partnerships"],"operationId":"reactivatePartnership","summary":"Reactivate a suspended partnership","description":"Reactivate a suspended partnership back to active status. Either party in the partnership can reactivate it. Sends a notification to the other party. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Partnership reactivated to active status","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{}}}}}},"400":{"description":"Partnership is not suspended"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/resend":{"post":{"tags":["Partnerships"],"operationId":"resendPartnershipInvite","summary":"Resend partnership invitation email","description":"Resend the invitation email for a pending partnership. Extends the invitation expiry by 30 days. Lab only - only the lab that initiated the partnership can resend.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation email resent with extended expiry","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"400":{"description":"Partnership is not pending"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership or invitation not found"},"429":{"description":"Partnership invitation send budget exceeded"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/share-all-patients":{"get":{"tags":["Partnerships"],"operationId":"getShareAllPatients","summary":"Get share-all-patients setting","description":"Check whether the share-all-patients setting is enabled for a partnership. Both parties of the partnership can read this setting.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Current share-all-patients boolean value","content":{"application/json":{"schema":{"type":"object","properties":{"shareAllPatients":{"type":"boolean"}},"required":["shareAllPatients"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Partnerships"],"operationId":"updateShareAllPatients","summary":"Update share-all-patients setting","description":"Toggle whether all patients are automatically shared with the lab partner. When disabled, labs only see patients with explicit lab access grants. Clinic only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"shareAllPatients":{"type":"boolean"}},"required":["shareAllPatients"],"additionalProperties":false}}}},"responses":{"200":{"description":"Setting updated","content":{"application/json":{"schema":{"type":"object","properties":{"shareAllPatients":{"type":"boolean"}},"required":["shareAllPatients"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"409":{"description":"Partnership is not active or changed concurrently"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/suspend":{"post":{"tags":["Partnerships"],"operationId":"suspendPartnership","summary":"Suspend an active partnership","description":"Suspend an active partnership. Either party can suspend. Sends a notification to the other party. The partnership can later be reactivated. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Partnership suspended","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{}}}}}},"400":{"description":"Partnership is not active"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"500":{"description":"Internal server error"}}}},"/partnerships/{id}/terminate":{"post":{"tags":["Partnerships"],"operationId":"terminatePartnership","summary":"Terminate a partnership","description":"Permanently terminate a partnership. Either party can terminate. Sends a notification to the other party. This action is not reversible. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"id","in":"path"}],"responses":{"200":{"description":"Partnership terminated permanently","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{}}}}}},"400":{"description":"Partnership is already terminated"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"500":{"description":"Internal server error"}}}},"/partnership-payment-settings/{partnershipId}":{"get":{"tags":["Partnership Payment Settings"],"operationId":"getPartnershipPaymentSettings","summary":"Get payment settings for a partnership","description":"Retrieve payment policy settings for a partnership, including the effective policy (partnership override or lab default) and Stripe Connect status. Available to both parties of the partnership.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"partnershipId","in":"path"}],"responses":{"200":{"description":"Payment settings with effective policy and Stripe Connect info","content":{"application/json":{"schema":{"type":"object","properties":{"paymentPolicy":{"type":["string","null"]},"invoiceDuePolicy":{"type":["string","null"],"enum":["net_days","day_of_month",null]},"invoiceTermDays":{"type":["integer","null"]},"invoiceDueDay":{"type":["integer","null"]},"invoiceDueMonthRule":{"type":["string","null"],"enum":["next_occurrence","following_month",null]},"creditHoldEnabled":{"type":["boolean","null"]},"effectivePolicy":{"type":"string"},"paymentUnavailable":{"type":"boolean"},"effectiveInvoiceDuePolicy":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["net_days"]},"termDays":{"type":"integer","minimum":1,"maximum":365}},"required":["kind","termDays"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["day_of_month"]},"day":{"type":"integer","minimum":1,"maximum":31},"monthRule":{"type":"string","enum":["next_occurrence","following_month"]}},"required":["kind","day","monthRule"],"additionalProperties":false}]},"effectiveInvoiceTermDays":{"type":["integer","null"]},"effectiveCreditHoldEnabled":{"type":"boolean"},"creditHoldActive":{"type":"boolean"},"labDefault":{"type":"string"},"labDefaultInvoiceDuePolicy":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["net_days"]},"termDays":{"type":"integer","minimum":1,"maximum":365}},"required":["kind","termDays"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["day_of_month"]},"day":{"type":"integer","minimum":1,"maximum":31},"monthRule":{"type":"string","enum":["next_occurrence","following_month"]}},"required":["kind","day","monthRule"],"additionalProperties":false}]},"labDefaultInvoiceTermDays":{"type":["integer","null"]},"labDefaultCreditHoldEnabled":{"type":"boolean"},"labStripeAccountId":{"type":["string","null"]},"autopayEnabled":{"type":"boolean"},"autopayConsentedAt":{"type":["string","null"],"format":"date-time"}},"required":["paymentPolicy","invoiceDuePolicy","invoiceTermDays","invoiceDueDay","invoiceDueMonthRule","creditHoldEnabled","effectivePolicy","paymentUnavailable","effectiveInvoiceDuePolicy","effectiveInvoiceTermDays","effectiveCreditHoldEnabled","creditHoldActive","labDefault","labDefaultInvoiceDuePolicy","labDefaultInvoiceTermDays","labDefaultCreditHoldEnabled","labStripeAccountId","autopayEnabled","autopayConsentedAt"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Partnership not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Partnership Payment Settings"],"operationId":"updatePartnershipPaymentSettings","summary":"Update payment settings for a partnership","description":"Set or clear a payment policy override for a partnership. Setting to null removes the override and falls back to the lab's default policy. Only the lab owner of the partnership may change it.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership ID","name":"partnershipId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"paymentPolicy":{"type":["string","null"],"enum":["pay_on_invoice","pay_in_advance",null]},"invoiceDuePolicy":{"type":["string","null"],"enum":["net_days","day_of_month",null]},"invoiceTermDays":{"type":["integer","null"],"minimum":1,"maximum":365},"invoiceDueDay":{"type":["integer","null"],"minimum":1,"maximum":31},"invoiceDueMonthRule":{"type":["string","null"],"enum":["next_occurrence","following_month",null]},"creditHoldEnabled":{"type":["boolean","null"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Settings updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"400":{"description":"Invalid payment policy"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Partnership not found"},"409":{"description":"Partnership is not active"},"500":{"description":"Internal server error"}}}},"/practice-groups":{"get":{"tags":["Practice Groups"],"operationId":"listPracticeGroups","summary":"List practice groups","description":"Retrieve a paginated list of practice groups for the current lab. Optionally includes partnership counts and inactive groups. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":true,"example":true},"required":false,"name":"includePartnershipCount","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"List of practice groups","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"useCustomExport":{"type":"boolean"},"exportConfig":{},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"partnershipCount":{"type":"number"}},"required":["id","labId","name","description","useCustomExport","sortOrder","isActive","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}},"post":{"tags":["Practice Groups"],"operationId":"createPracticeGroup","summary":"Create a practice group","description":"Create a new practice group to organize clinic partnerships. Supports custom export configurations. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"useCustomExport":{"type":"boolean","default":false},"exportConfig":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":100},"enabled":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["key","label"],"additionalProperties":false},"default":[]},"format":{"type":"string","enum":["csv","xlsx","pdf"],"default":"csv"},"includeHeaders":{"type":"boolean","default":true},"dateFormat":{"type":"string","maxLength":50,"default":"YYYY-MM-DD"}},"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Practice group created","content":{"application/json":{"schema":{"type":"object","properties":{"practiceGroup":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}}},"/practice-groups/{id}":{"get":{"tags":["Practice Groups"],"operationId":"getPracticeGroup","summary":"Get a practice group by ID","description":"Retrieve a practice group with partnership count. Optionally includes the full list of partnerships in the group. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"false"},"required":false,"name":"includePartnerships","in":"query"}],"responses":{"200":{"description":"Practice group","content":{"application/json":{"schema":{"type":"object","properties":{"practiceGroup":{}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Practice group not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Practice Groups"],"operationId":"updatePracticeGroup","summary":"Update a practice group","description":"Update one or more fields of a practice group. Supports partial updates. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"useCustomExport":{"type":"boolean","default":false},"exportConfig":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":100},"enabled":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["key","label"],"additionalProperties":false},"default":[]},"format":{"type":"string","enum":["csv","xlsx","pdf"],"default":"csv"},"includeHeaders":{"type":"boolean","default":true},"dateFormat":{"type":"string","maxLength":50,"default":"YYYY-MM-DD"}},"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Practice group updated","content":{"application/json":{"schema":{"type":"object","properties":{"practiceGroup":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Practice group not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Practice Groups"],"operationId":"deletePracticeGroup","summary":"Deactivate a practice group","description":"Soft-delete a practice group by setting it to inactive. Active partnerships in the group are unassigned first; terminal epochs retain their historical grouping. Lab manager only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Practice group deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"practiceGroup":{},"message":{"type":"string"}},"required":["message"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Practice group not found"},"500":{"description":"Internal server error"}}}},"/practice-groups/{id}/partnerships":{"get":{"tags":["Practice Groups"],"operationId":"listPracticeGroupPartnerships","summary":"List partnerships in a practice group","description":"Retrieve active partnerships assigned to a specific practice group, including clinic names and logos. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"List of partnerships","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"status":{"type":"string"},"initiatedBy":{"type":["string","null"]},"practiceGroupId":{"type":["string","null"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"clinicName":{"type":["string","null"]},"clinicLogo":{"type":["string","null"]}},"required":["id","labId","clinicId","status","initiatedBy","practiceGroupId","notes","createdAt","updatedAt","clinicName","clinicLogo"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Practice group not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Practice Groups"],"operationId":"assignPracticeGroupPartnerships","summary":"Assign partnerships to a practice group","description":"Assign one or more active partnerships to an active practice group. All partnerships must belong to the current lab. Lab manager only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"partnershipIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["partnershipIds"]}}}},"responses":{"200":{"description":"Partnerships assigned","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"message":{"type":"string"}},"required":["data","message"]}}}},"400":{"description":"Invalid request or partnerships not found"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Practice group not found"},"500":{"description":"Internal server error"}}}},"/practice-groups/{id}/partnerships/{partnershipId}":{"delete":{"tags":["Practice Groups"],"operationId":"removePracticeGroupPartnership","summary":"Remove a partnership from a practice group","description":"Remove a single active partnership from a practice group by setting its practiceGroupId to null. The partnership itself is not deleted. Lab manager only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"partnershipId","in":"path"}],"responses":{"200":{"description":"Partnership removed from group","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{},"message":{"type":"string"}},"required":["message"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Practice group or partnership not found"},"500":{"description":"Internal server error"}}}},"/organization/onboarding":{"get":{"tags":["Organization Onboarding"],"operationId":"getOrganizationOnboarding","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Persisted onboarding snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"onboarding":{}}}}}},"403":{"description":"Organization owner required"},"404":{"description":"Onboarding is not required"}}}},"/organization/onboarding/step":{"put":{"tags":["Organization Onboarding"],"operationId":"saveOrganizationOnboardingStep","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"step":{"type":"string","enum":["identity","regional","location","tax","subscription"]},"direction":{"type":"string","enum":["forward","back"]},"data":{}},"required":["step","direction"],"additionalProperties":false}}}},"responses":{"200":{"description":"Updated onboarding snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"onboarding":{}}}}}},"400":{"description":"Invalid or out-of-order onboarding step"},"403":{"description":"Organization owner required"},"404":{"description":"Onboarding is not required"},"409":{"description":"Organization identity conflicts"}}}},"/organization/onboarding/complete":{"post":{"tags":["Organization Onboarding"],"operationId":"completeClinicOrganizationOnboarding","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Clinic onboarding completed"},"400":{"description":"Required clinic details are incomplete"},"403":{"description":"Clinic owner required"},"404":{"description":"Onboarding is not required"}}}},"/organization/onboarding/subscription-checkout":{"post":{"tags":["Organization Onboarding"],"operationId":"createOnboardingSubscriptionCheckout","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["base","growth","scale"]},"interval":{"type":"string","enum":["month","year"]},"returnUrl":{"type":"string","format":"uri"},"cancelUrl":{"type":"string","format":"uri"}},"required":["plan","interval","returnUrl","cancelUrl"],"additionalProperties":false}}}},"responses":{"200":{"description":"Stripe Checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"Plan selection is not persisted"},"403":{"description":"Lab owner required"},"404":{"description":"Onboarding is not required"},"409":{"description":"The lab already has an active or unreviewed platform subscription"},"502":{"description":"Stripe Checkout could not be created"},"503":{"description":"Billing verification is temporarily unavailable"}}}},"/organization/check-availability":{"get":{"tags":["Organization"],"operationId":"checkOrgNameAvailability","summary":"Check organization name availability","description":"Check whether an organization name is already taken. No authentication required. Used during onboarding.","security":[],"parameters":[{"schema":{"type":"string","example":"My Lab"},"required":false,"name":"name","in":"query"},{"schema":{"type":"string","example":"lab"},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"Availability result","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean"},"name":{"type":"string"},"message":{"type":"string"}},"required":["available"]}}}},"400":{"description":"Invalid input"},"500":{"description":"Internal server error"}}}},"/organization/check-slug":{"get":{"tags":["Organization"],"operationId":"checkOrgSlugAvailability","summary":"Check organization slug availability","description":"Check whether an organization slug is already taken. Requires user authentication. Used during onboarding.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-lab"},"required":false,"name":"slug","in":"query"}],"responses":{"200":{"description":"Slug availability","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean"}},"required":["available"]}}}},"400":{"description":"Invalid input"},"500":{"description":"Internal server error"}}}},"/organization/create":{"post":{"tags":["Organization"],"operationId":"createOrganization","summary":"Create a new organization","description":"Create a new lab or clinic organization. The current user becomes the owner. Labs get default treatment phases and steps. Sets the new org as the user's active organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":200},"slug":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,48}[a-z0-9])?$"},"type":{"type":"string","enum":["clinic","lab"]},"phone":{"type":"string","maxLength":50},"address":{"type":"string","maxLength":500},"city":{"type":"string","maxLength":100},"postcode":{"type":"string","maxLength":20},"website":{"anyOf":[{"type":"string","maxLength":500,"format":"uri"},{"type":"string","enum":[""]}]}},"required":["name","type"],"additionalProperties":false}}}},"responses":{"200":{"description":"Organization created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"organizationType":{"type":"string"}},"required":["id","name","slug","organizationType"]}},"required":["success","organization"]}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"409":{"description":"Organization slug already exists"},"500":{"description":"Internal server error"}}}},"/organization/join-request":{"post":{"tags":["Organization"],"operationId":"createJoinRequest","summary":"Request to join an organization","description":"Submit a request to join an organization. Sends a notification to the organization's owners. Prevents duplicate requests.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string"},"message":{"type":"string"}},"required":["organizationId"]}}}},"responses":{"201":{"description":"Join request created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"joinRequest":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"createdAt":{}},"required":["id","status"]}},"required":["success","joinRequest"]}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"404":{"description":"Organization not found"},"409":{"description":"Already a member or request pending"},"429":{"description":"Join request creation budget exceeded"},"500":{"description":"Internal server error"}}},"get":{"tags":["Organization"],"operationId":"listJoinRequests","summary":"List pending join requests","description":"List all pending join requests for the current organization. Only organization owners can view these. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of join requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"status":{"type":"string"},"message":{"type":["string","null"]},"createdAt":{},"userName":{"type":"string"},"userEmail":{"type":"string"}},"required":["id","userId","status","message","userName","userEmail"]}}},"required":["requests"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}}},"/organization/join-request/{id}":{"patch":{"tags":["Organization"],"operationId":"processJoinRequest","summary":"Approve or reject a join request","description":"Approve or reject a pending join request. Approved users become staff members. Sends a notification to the requester. Owner only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["approve","reject"]}},"required":["action"]}}}},"responses":{"200":{"description":"Join request processed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"Invalid action"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Join request not found"},"500":{"description":"Internal server error"}}}},"/organization/join-request/{id}/resend":{"post":{"tags":["Organization"],"operationId":"resendJoinRequest","summary":"Resend a join request notification","description":"Resend notification emails to organization owners about a pending join request. Only the request author can resend.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Notification resent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Join request not found"},"429":{"description":"Join request reminder budget exceeded"},"500":{"description":"Internal server error"}}}},"/organization/join-search":{"get":{"tags":["Organization"],"operationId":"searchOrganizationsToJoin","summary":"Search organizations to join","description":"Search for organizations the current user could request to join. Includes existing request and membership status for each result. Used during onboarding.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"search term"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","example":"lab"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"city":{"type":["string","null"]},"organizationType":{"type":["string","null"]},"latitude":{"type":["string","null"]},"longitude":{"type":["string","null"]},"hasRequest":{"type":"boolean"},"isMember":{"type":"boolean"}},"required":["id","name","city","organizationType","latitude","longitude","hasRequest","isMember"]}}},"required":["organizations"]}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/organization/list-by-type":{"get":{"tags":["Organization"],"operationId":"listOrganizationsByType","summary":"List organizations by type","description":"List all organizations of a given type (lab or clinic). Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"lab"},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"List of organizations","content":{"application/json":{"schema":{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":["string","null"]},"city":{"type":["string","null"]},"organizationType":{"type":["string","null"]}},"required":["id","name","slug","city","organizationType"]}}},"required":["organizations"]}}}},"400":{"description":"Invalid type"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}}},"/organization/my-memberships":{"get":{"tags":["Organization"],"operationId":"listMyMemberships","summary":"List current user's memberships","description":"List all organizations the current user is a member of, including role, type, and logo. Used for org switching.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of memberships","content":{"application/json":{"schema":{"type":"object","properties":{"memberships":{"type":"array","items":{"type":"object","properties":{"organizationId":{"type":"string"},"organizationName":{"type":"string"},"organizationType":{"type":["string","null"]},"slug":{"type":["string","null"]},"logo":{"type":["string","null"]},"city":{"type":["string","null"]},"role":{"type":"string"}},"required":["organizationId","organizationName","organizationType","slug","logo","city","role"]}}},"required":["memberships"]}}}},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/organization/my-pending-requests":{"get":{"tags":["Organization"],"operationId":"listMyPendingRequests","summary":"List current user's pending join requests","description":"List all pending join requests submitted by the current user. Used for the org picker page.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of pending join requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orgName":{"type":"string"},"orgType":{"type":["string","null"]},"createdAt":{}},"required":["id","orgName","orgType"]}}},"required":["requests"]}}}},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/organization/public-search":{"get":{"tags":["Organization"],"operationId":"publicSearchOrganizations","summary":"Public organization search","description":"Search organizations by name and type without authentication. Returns minimal info (id, name, city). Requires at least 2 characters. Used for pre-auth flows.","security":[],"parameters":[{"schema":{"type":"string","example":"search term"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","example":"lab"},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"city":{"type":["string","null"]}},"required":["id","name","city"]}}},"required":["organizations"]}}}},"400":{"description":"Invalid input"},"500":{"description":"Internal server error"}}}},"/organization/search":{"get":{"tags":["Organization"],"operationId":"searchOrganizations","summary":"Search organizations","description":"Search organizations by name with fuzzy matching. Can filter to only partnered organizations. Includes pending join request status. Requires user authentication.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"search term"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","example":"lab"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"partneredWith","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"true"},"required":false,"name":"includePending","in":"query"}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"city":{"type":["string","null"]},"slug":{"type":["string","null"]},"currency":{"type":"string"},"hasRequest":{"type":"boolean"},"partnershipStatus":{"type":["string","null"],"enum":["active","pending",null]},"provisioningStatus":{"type":"string","enum":["claimed","pending_claim","managed","abandoned"]}},"required":["id","name","city","slug","currency","hasRequest","provisioningStatus"]}}},"required":["organizations"]}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/organization/{id}":{"get":{"tags":["Organization"],"operationId":"getOrganization","summary":"Get an organization by ID","description":"Retrieve basic organization info. Access is limited to your own organization or partner organizations. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Organization","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":["string","null"]},"type":{"type":["string","null"]},"city":{"type":["string","null"]}},"required":["id","name","slug","type","city"]}},"required":["organization"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Organization not found"},"500":{"description":"Internal server error"}}}},"/organization/{id}/members":{"get":{"tags":["Organization"],"operationId":"listOrganizationMembers","summary":"List members of an organization","description":"Retrieve members of an organization. Accessible to the org itself or active partner organizations. Supports search and role filtering. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","example":"owner"},"required":false,"name":"role","in":"query"}],"responses":{"200":{"description":"List of members","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"}},"required":["id","name","email","role"]}}},"required":["members"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}}},"/organization/{id}/profile":{"get":{"tags":["Organization"],"operationId":"getOrganizationProfile","summary":"Get public profile of an organization","description":"Retrieve the public-facing profile of a discoverable organization, including description, cover image, gallery, and product categories (for labs). Requires authentication.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Organization profile","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"organizationType":{"type":["string","null"]},"logo":{"type":["string","null"]},"description":{"type":["string","null"]},"hasCoverImage":{"type":"boolean"},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"latitude":{"type":["string","null"]},"longitude":{"type":["string","null"]},"gallery":{"type":"array","items":{}},"categories":{"type":"array","items":{}}},"required":["id","name","organizationType","logo","description","hasCoverImage","email","phone","website","latitude","longitude","gallery","categories"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Organization not found"},"500":{"description":"Internal server error"}}}},"/organization/subscription":{"get":{"tags":["Organization"],"operationId":"getCurrentOrganizationSubscription","summary":"Get current organization subscription","description":"Returns the latest subscription record for the active lab organization. Read-only and available even when billing is past due.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Latest organization subscription or null","content":{"application/json":{"schema":{"type":"object","properties":{"subscription":{"type":["object","null"],"properties":{"id":{"type":"string"},"plan":{"type":"string"},"status":{"type":["string","null"]},"periodStart":{"type":["string","null"]},"periodEnd":{"type":["string","null"]},"cancelAtPeriodEnd":{"type":["boolean","null"]},"seats":{"type":["number","null"]},"trialStart":{"type":["string","null"]},"trialEnd":{"type":["string","null"]},"hasBillingCustomer":{"type":"boolean"},"billingInterval":{"type":["string","null"]},"billingCurrency":{"type":["string","null"]},"catalogVersion":{"type":["string","null"]},"storageAddOnUnits":{"type":"integer","minimum":0},"pastDueAt":{"type":["string","null"]},"pastDueGraceEndsAt":{"type":["string","null"]},"pendingStorageAddOnUnits":{"type":["integer","null"],"minimum":0},"pendingStorageAddOnAt":{"type":["string","null"]},"pendingPlan":{"type":["string","null"],"enum":["base","growth","scale",null]},"pendingPlanBillingInterval":{"type":["string","null"],"enum":["month","year",null]},"pendingPlanAt":{"type":["string","null"]},"usage":{"type":"object","properties":{"cases":{"type":"object","properties":{"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"used":{"type":"integer","minimum":0},"included":{"type":["integer","null"],"minimum":0},"alert":{"type":"string","enum":["normal","warning","critical","over"]},"nonBlocking":{"type":"boolean","enum":[true]}},"required":["windowStart","windowEnd","used","included","alert","nonBlocking"]},"storage":{"type":"object","properties":{"usedBytes":{"type":"number","minimum":0},"includedBytes":{"type":"number","minimum":0},"addOnBytes":{"type":"number","minimum":0},"alert":{"type":"string","enum":["normal","warning","critical","over"]},"nonBlocking":{"type":"boolean","enum":[true]}},"required":["usedBytes","includedBytes","addOnBytes","alert","nonBlocking"]}},"required":["cases","storage"]}},"required":["id","plan","status","periodStart","periodEnd","cancelAtPeriodEnd","seats","trialStart","trialEnd","hasBillingCustomer","billingInterval","billingCurrency","catalogVersion","storageAddOnUnits","pastDueAt","pastDueGraceEndsAt","pendingStorageAddOnUnits","pendingStorageAddOnAt","pendingPlan","pendingPlanBillingInterval","pendingPlanAt","usage"]}},"required":["subscription"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Lab organizations only"}}}},"/organization/subscription/storage-add-on":{"put":{"tags":["Organization"],"operationId":"updateOrganizationSubscriptionStorageAddOn","summary":"Update organization subscription storage add-on","description":"Applies storage increases immediately with proration and schedules decreases for the next subscription renewal. GuideLab, not the Stripe portal, owns add-on quantity changes.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"units":{"type":"integer","minimum":0,"maximum":1000}},"required":["units"]}}}},"responses":{"200":{"description":"Updated or scheduled storage quantity","content":{"application/json":{"schema":{"type":"object","properties":{"storageAddOnUnits":{"type":"integer","minimum":0},"pendingStorageAddOnUnits":{"type":["integer","null"],"minimum":0},"pendingStorageAddOnAt":{"type":["string","null"]},"change":{"type":"string","enum":["unchanged","immediate_increase","scheduled_decrease","scheduled_decrease_cancelled"]}},"required":["storageAddOnUnits","pendingStorageAddOnUnits","pendingStorageAddOnAt","change"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Lab owners only"},"409":{"description":"No mutable platform subscription found"},"500":{"description":"Stripe or catalog configuration unavailable"}}}},"/organization/subscription/billing-portal":{"post":{"tags":["Organization"],"operationId":"createOrganizationBillingPortalSession","summary":"Create organization billing portal session","description":"Creates a Stripe billing portal session for the active lab organization so owners can update cards and manage subscription billing.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"Billing portal URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Lab owners only"},"404":{"description":"No billing customer found"}}}},"/organization/join-code":{"get":{"tags":["Organization"],"operationId":"getJoinCode","summary":"Get the organization's active join code","description":"Returns the current active shareable join code for the organization, or null if none exists. Owners and admins only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"The active join code, or null","content":{"application/json":{"schema":{"type":"object","properties":{"joinCode":{"type":["object","null"],"properties":{"code":{"type":"string"},"role":{"type":"string","enum":["staff","admin"]},"expiresAt":{"type":["string","null"]},"maxUses":{"type":["integer","null"]},"useCount":{"type":"integer"},"createdAt":{"type":"string"}},"required":["code","role","expiresAt","maxUses","useCount","createdAt"]}},"required":["joinCode"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}},"post":{"tags":["Organization"],"operationId":"createJoinCode","summary":"Generate or rotate the organization's join code","description":"Generates a fresh shareable join code, revoking any previous active code. Owners and admins only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["staff","admin"],"default":"staff"},"expiresInDays":{"type":["integer","null"],"exclusiveMinimum":0,"maximum":365},"maxUses":{"type":["integer","null"],"exclusiveMinimum":0,"maximum":10000}},"additionalProperties":false}}}},"responses":{"201":{"description":"Join code created","content":{"application/json":{"schema":{"type":"object","properties":{"joinCode":{"type":"object","properties":{"code":{"type":"string"},"role":{"type":"string","enum":["staff","admin"]},"expiresAt":{"type":["string","null"]},"maxUses":{"type":["integer","null"]},"useCount":{"type":"integer"},"createdAt":{"type":"string"}},"required":["code","role","expiresAt","maxUses","useCount","createdAt"]}},"required":["joinCode"]}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Organization"],"operationId":"revokeJoinCode","summary":"Revoke the organization's active join code","description":"Revokes the current active join code so it can no longer be redeemed. Owners and admins only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Join code revoked","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}}},"/organization/join-code/redeem":{"post":{"tags":["Organization"],"operationId":"redeemJoinCode","summary":"Redeem a join code to join an organization","description":"Redeems a shareable join code and adds the current user as a member of the organization. Returns 404 with error 'invalid_code' for unknown codes so clients can fall back to invitation-id acceptance.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":4,"maxLength":64}},"required":["code"]}}}},"responses":{"200":{"description":"Joined (or already a member)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"organizationId":{"type":"string"},"organizationType":{"type":"string","enum":["lab","clinic"]},"alreadyMember":{"type":"boolean"}},"required":["success","organizationId","organizationType"]}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"404":{"description":"Invalid code"},"409":{"description":"Code revoked or usage limit reached"},"410":{"description":"Code expired"},"500":{"description":"Internal server error"}}}},"/organization/closure":{"get":{"tags":["Organization"],"operationId":"getOrganizationClosureStatus","summary":"Get organization closure status","description":"Returns the durable closure receipt for an active or closing organization. A narrow owner-membership check remains available after the organization is fenced.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Organization closure status","content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string"},"lifecycleStatus":{"type":"string","enum":["active","closing","closed"]},"closureRequestedAt":{"type":["string","null"]},"closureReason":{"type":["string","null"]},"closedAt":{"type":["string","null"]},"profileRedactionDueAt":{"type":["string","null"]},"profileRedactedAt":{"type":["string","null"]},"job":{"type":["object","null"],"properties":{"status":{"type":"string","enum":["pending","processing","failed","completed"]},"stage":{"type":"string"},"attempts":{"type":"number"},"availableAt":{"type":"string"},"lastError":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"failedAt":{"type":["string","null"]}},"required":["status","stage","attempts","availableAt","lastError","completedAt","failedAt"]}},"required":["organizationId","lifecycleStatus","closureRequestedAt","closureReason","closedAt","profileRedactionDueAt","profileRedactedAt","job"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Organization owner access required"},"404":{"description":"Organization not found"}}},"post":{"tags":["Organization"],"operationId":"requestOrganizationClosure","summary":"Request organization closure","description":"Fences the organization immediately and starts its bounded, resumable closure workflow. Replays are idempotent.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":2000}},"required":["reason"]}}}},"responses":{"202":{"description":"Closure accepted or already in progress","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["requested","already_requested"]}},"required":["status"]}}}},"400":{"description":"Invalid closure reason"},"401":{"description":"Unauthorized"},"403":{"description":"Organization owner access required"},"404":{"description":"Organization not found"},"409":{"description":"In-flight orders or shipments block closure"},"500":{"description":"Internal server error"}}}},"/organization/retention-holds":{"get":{"tags":["Organization"],"operationId":"listOrganizationRetentionHolds","summary":"List retention holds","description":"Lists a bounded set of immutable retention-hold evidence placed by the active organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["live","released","all"],"default":"live"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Retention holds","content":{"application/json":{"schema":{"type":"object","properties":{"holds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"holdingOrganizationId":{"type":"string"},"matterKey":{"type":"string"},"operation":{"type":"string","enum":["all","identity_erasure","object_retirement","organization_closure","conversation_deletion"]},"reason":{"type":"string"},"scope":{"type":"object","properties":{"kind":{"type":"string","enum":["organization","partnership","patient","order","invoice","statement","file","conversation","conversation_attachment","scan_inbox_item"]},"id":{"type":"string","minLength":1,"maxLength":200}},"required":["kind","id"]},"placedAt":{"type":"string","format":"date-time"},"placedBy":{"type":"string"},"releasedAt":{"type":["string","null"],"format":"date-time"},"releasedBy":{"type":["string","null"]},"reviewDueAt":{"type":["string","null"],"format":"date-time"},"platformCustodyAt":{"type":["string","null"],"format":"date-time"}},"required":["id","holdingOrganizationId","matterKey","operation","reason","scope","placedAt","placedBy","releasedAt","releasedBy","reviewDueAt","platformCustodyAt"]}}},"required":["holds"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Organization manager access required"},"500":{"description":"Internal server error"}}},"post":{"tags":["Organization"],"operationId":"placeOrganizationRetentionHold","summary":"Place a retention hold","description":"Places immutable, controller-authorized hold evidence. Matter-key replays are idempotent only when the evidence is identical.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"matterKey":{"type":"string","minLength":1,"maxLength":200},"operation":{"type":"string","enum":["all","identity_erasure","object_retirement","organization_closure","conversation_deletion"]},"reason":{"type":"string","minLength":1,"maxLength":2000},"scope":{"type":"object","properties":{"kind":{"type":"string","enum":["organization","partnership","patient","order","invoice","statement","file","conversation","conversation_attachment","scan_inbox_item"]},"id":{"type":"string","minLength":1,"maxLength":200}},"required":["kind","id"]},"reviewDueAt":{"type":["string","null"],"format":"date-time"}},"required":["matterKey","operation","reason","scope"]}}}},"responses":{"200":{"description":"Retention hold placed or replayed","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["placed","already_placed"]},"hold":{"type":"object","properties":{"id":{"type":"string"},"holdingOrganizationId":{"type":"string"},"matterKey":{"type":"string"},"operation":{"type":"string","enum":["all","identity_erasure","object_retirement","organization_closure","conversation_deletion"]},"reason":{"type":"string"},"scope":{"type":"object","properties":{"kind":{"type":"string","enum":["organization","partnership","patient","order","invoice","statement","file","conversation","conversation_attachment","scan_inbox_item"]},"id":{"type":"string","minLength":1,"maxLength":200}},"required":["kind","id"]},"placedAt":{"type":"string","format":"date-time"},"placedBy":{"type":"string"},"releasedAt":{"type":["string","null"],"format":"date-time"},"releasedBy":{"type":["string","null"]},"reviewDueAt":{"type":["string","null"],"format":"date-time"},"platformCustodyAt":{"type":["string","null"],"format":"date-time"}},"required":["id","holdingOrganizationId","matterKey","operation","reason","scope","placedAt","placedBy","releasedAt","releasedBy","reviewDueAt","platformCustodyAt"]}},"required":["status","hold"]}}}},"400":{"description":"Invalid hold evidence or review date"},"401":{"description":"Unauthorized"},"403":{"description":"Organization does not control the requested scope"},"409":{"description":"Matter key is bound to different evidence"},"500":{"description":"Internal server error"}}}},"/organization/retention-holds/{id}/release":{"post":{"tags":["Organization"],"operationId":"releaseOrganizationRetentionHold","summary":"Release a retention hold","description":"Releases a hold owned by the active organization while preserving its immutable placement and release evidence.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Retention hold released or already released","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["released","already_released"]}},"required":["status"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Organization manager access required"},"404":{"description":"Retention hold not found"},"500":{"description":"Internal server error"}}}},"/settings-defaults/initialize":{"post":{"tags":["Organization Settings"],"operationId":"initializeOrganizationSettingsDefaults","summary":"Initialize organization settings defaults","description":"Idempotently applies any unapplied settings-default revision using stable keys. The organization type and revision are read from a locked database row. New organizations run the same initializer during creation; this manager command repairs legacy or partially initialized organizations without recreating defaults intentionally removed after a revision was applied.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Settings defaults initialized","content":{"application/json":{"schema":{"type":"object","properties":{"initialized":{"type":"object","properties":{"shadeSystems":{"type":"number"},"implantSystems":{"type":"number"},"orderFilterPresets":{"type":"number"},"reports":{"type":"number"},"qcSettings":{"type":"number"},"productionSettings":{"type":"number"},"receivingCategories":{"type":"number"},"receivingSources":{"type":"number"},"inventoryCategories":{"type":"number"},"inventoryAdjustmentReasons":{"type":"number"}},"required":["shadeSystems","implantSystems","orderFilterPresets","reports","qcSettings","productionSettings","receivingCategories","receivingSources","inventoryCategories","inventoryAdjustmentReasons"]}},"required":["initialized"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Organization owner or admin required"},"500":{"description":"Internal server error"}}}},"/team/members":{"get":{"tags":["Team"],"operationId":"listTeamMembers","summary":"List team members","description":"Returns all members of the current organization with their name, email, role, and avatar.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of team members with user details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"image":{"type":["string","null"]},"role":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","userId","name","email","image","role","createdAt"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/team/members/{id}":{"patch":{"tags":["Team"],"operationId":"updateTeamMemberRole","summary":"Update team member role","description":"Changes a team member's non-owner role. Organization owners and admins may update roles; doctor is clinic-only. Ownership moves only through the transfer endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Member ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Member ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","staff","doctor"]}},"required":["role"]}}}},"responses":{"200":{"description":"Member role updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"organizationId":{"type":"string"},"role":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","userId","organizationId","role","createdAt","updatedAt"]}},"required":["member"]}}}},"400":{"description":"Invalid request, cannot change owner role, or cannot change own role"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"},"404":{"description":"Member not found in this organization"},"429":{"description":"Password reset email budget exceeded"}}},"delete":{"tags":["Team"],"operationId":"removeTeamMember","summary":"Remove team member","description":"Removes a member from the organization. Organization owners and admins may remove non-owner members, but cannot remove themselves.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Member ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Member ID","name":"id","in":"path"}],"responses":{"200":{"description":"Member removed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Cannot remove owner or yourself"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"},"404":{"description":"Member not found in this organization"}}}},"/team/members/{id}/transfer-ownership":{"post":{"tags":["Team"],"operationId":"transferOrganizationOwnership","summary":"Transfer organization ownership","description":"Atomically promotes the selected member to owner and demotes the current owner to admin. Only the current owner may transfer ownership.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Member ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Member ID","name":"id","in":"path"}],"responses":{"200":{"description":"Ownership transferred","content":{"application/json":{"schema":{"type":"object","properties":{"ownerMemberId":{"type":"string"},"previousOwnerMemberId":{"type":"string"}},"required":["ownerMemberId","previousOwnerMemberId"]}}}},"400":{"description":"Invalid ownership target"},"401":{"description":"Unauthorized"},"403":{"description":"Current organization owner required"},"404":{"description":"Target member not found"}}}},"/team/members/{id}/send-password-reset":{"post":{"tags":["Team"],"operationId":"sendPasswordResetEmail","summary":"Send password reset email to team member","description":"Triggers a password reset email for the specified team member via Better Auth. Organization owners and admins may send password resets.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Member ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Member ID","name":"id","in":"path"}],"responses":{"200":{"description":"Password reset email sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"},"404":{"description":"Member not found in this organization"}}}},"/team/invitations":{"get":{"tags":["Team"],"operationId":"listPendingInvitations","summary":"List pending invitations","description":"Returns all pending team invitations for the current organization, including the inviter's name and expiration date.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of pending invitations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"},"status":{"type":"string"},"inviterId":{"type":"string"},"inviterName":{"type":"string"},"expiresAt":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","email","role","status","inviterId","inviterName","expiresAt","createdAt"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"}}},"post":{"tags":["Team"],"operationId":"createTeamInvitation","summary":"Create and send team invitation","description":"Sends an invitation for an assignable organization role. Owners and admins may invite; doctor is clinic-only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","staff","doctor"]}},"required":["email","role"]}}}},"responses":{"201":{"description":"Invitation created and email sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"invitation":{}},"required":["success"]}}}},"400":{"description":"Invalid request, user already a member, or pending invitation exists"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"}}}},"/team/invitations/{id}/cancel":{"post":{"tags":["Team"],"operationId":"cancelTeamInvitation","summary":"Cancel a pending invitation","description":"Cancels a pending invitation so the recipient can no longer accept it. Organization owners and admins may cancel pending invitations.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invitation ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invitation ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation canceled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invitation is not in pending status"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"},"404":{"description":"Invitation not found in this organization"}}}},"/team/invitations/{id}/resend":{"post":{"tags":["Team"],"operationId":"resendTeamInvitation","summary":"Resend a pending invitation","description":"Re-sends a pending invitation. Organization owners and admins may resend invitations whose role is still assignable to the organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invitation ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invitation ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation email resent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"invitation":{}},"required":["success"]}}}},"400":{"description":"Invitation is not in pending status"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Forbidden — organization manager required"},"404":{"description":"Invitation not found in this organization"},"409":{"description":"Invitation role is no longer assignable"},"429":{"description":"Invitation email budget exceeded"}}}},"/user/phone":{"put":{"tags":["Users"],"operationId":"updateUserPhone","summary":"Update current user phone number","description":"Updates the authenticated user's phone number. Validates and stores in E.164 format. Pass an empty string to remove.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string"}},"required":["phoneNumber"]}}}},"responses":{"200":{"description":"Phone number updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid phone number format"},"401":{"description":"Unauthorized — valid session required"}}}},"/user/language":{"put":{"tags":["Users"],"operationId":"updateUserLanguage","summary":"Update current user language preference","description":"Sets the authenticated user's preferred UI language. Supported: en, es, it, zh.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"language":{"type":"string","enum":["en","es","it","zh"]}},"required":["language"]}}}},"responses":{"200":{"description":"Language preference updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid language code"},"401":{"description":"Unauthorized — valid session required"}}}},"/user/avatar":{"post":{"tags":["Users"],"operationId":"uploadUserAvatar","summary":"Upload current user avatar","description":"Upload a new avatar image for the authenticated user. Replaces any existing avatar. Accepts JPEG, PNG, WebP, or GIF up to 5MB.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Avatar uploaded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string"}},"required":["image"]}}}},"400":{"description":"Invalid file type or file too large"},"401":{"description":"Unauthorized — valid session required"},"500":{"description":"Internal server error"}}},"get":{"tags":["Users"],"operationId":"getUserAvatar","summary":"Serve a user avatar","description":"Serve a user's avatar image from R2 storage. Defaults to the authenticated user. Accessible by self, members of shared organizations, and users in partnership-linked organizations.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"userId","in":"query"}],"responses":{"200":{"description":"Avatar image binary"},"403":{"description":"Not allowed to view this avatar"},"404":{"description":"User or avatar not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Users"],"operationId":"deleteUserAvatar","summary":"Remove current user avatar","description":"Remove the authenticated user's avatar from storage and clear the image reference on the user record.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Avatar removed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"No avatar to delete"},"401":{"description":"Unauthorized — valid session required"},"500":{"description":"Internal server error"}}}},"/user/notification-preferences":{"get":{"tags":["Users"],"operationId":"getUserNotificationPreferences","summary":"Get notification preferences","description":"Returns the authenticated user's notification channel preferences for orders, partnerships, and chat. Returns defaults if no custom preferences have been set.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Notification preferences object","content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"object","properties":{"orders":{},"partnerships":{},"chat":{},"finance":{},"team":{},"inventory":{}}}},"required":["preferences"]}}}},"401":{"description":"Unauthorized — valid session required"}}},"put":{"tags":["Users"],"operationId":"updateUserNotificationPreferences","summary":"Update notification preferences","description":"Sets the authenticated user's notification channel preferences for orders (including per-event overrides), partnerships, and chat. Supports email, SMS, WhatsApp, and push channels.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"object","properties":{"orders":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"},"events":{"type":"object","properties":{"order.status_changed":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"order.assigned":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"order.task_assigned":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"order.phase_advance_requested":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"order.files_reminder":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]}}}},"required":["email","sms","whatsapp","push"]},"partnerships":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"chat":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"finance":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"team":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]},"inventory":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"},"push":{"type":"boolean"}},"required":["email","sms","whatsapp","push"]}},"required":["orders","partnerships","chat","finance","team"]}},"required":["preferences"]}}}},"responses":{"200":{"description":"Preferences saved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid preferences structure"},"401":{"description":"Unauthorized — valid session required"}}}},"/admin/users":{"get":{"tags":["Admin"],"operationId":"adminListUsers","summary":"List all users","description":"Retrieve a paginated list of all platform users with their organization memberships. Platform admin access required. Supports search by email and role filtering.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"0"},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"user"},"required":false,"name":"role","in":"query"},{"schema":{"type":"string","example":"desc"},"required":false,"name":"sortDirection","in":"query"}],"responses":{"200":{"description":"Paginated list of users with organization memberships","content":{"application/json":{"schema":{"type":"object","properties":{"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"image":{"type":["string","null"]},"role":{"type":"string"},"banned":{"type":["boolean","null"]},"createdAt":{},"organizations":{"type":"array","items":{}}},"required":["id","name","email","image","role","banned","organizations"]}},"total":{"type":"number"}},"required":["users","total"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"}}}},"/admin/users/{id}":{"get":{"tags":["Admin"],"operationId":"adminGetUser","summary":"Get user by ID","description":"Retrieve a single user's full details including all organization memberships. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"User details with memberships","content":{"application/json":{"schema":{"type":"object","properties":{"user":{},"memberships":{"type":"array","items":{}}},"required":["memberships"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"User not found"},"409":{"description":"Organization ownership must be transferred first"}}}},"/admin/users/{id}/send-password-reset":{"post":{"tags":["Admin"],"operationId":"adminSendPasswordReset","summary":"Send password reset email for a user","description":"Trigger a password reset email for a specific user via Better Auth's forget-password flow. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Password reset email sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"User not found"},"409":{"description":"Organization ownership must be transferred first"}}}},"/admin/users/{id}/anonymize":{"post":{"tags":["Admin"],"operationId":"adminAnonymizeUser","summary":"Anonymize (GDPR-erase) a user","description":"Scrub a user's PII and revoke all access while retaining their records — orders, invoices, files, and messages reference the user and cannot be deleted. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"User anonymized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"User not found"}}}},"/admin/organizations":{"get":{"tags":["Admin"],"operationId":"adminListOrganizations","summary":"List all organizations","description":"Retrieve a paginated list of all organizations with member counts. Platform admin access required. Supports search and type filtering.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"1"},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"lab"},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"Paginated list of organizations with member counts","content":{"application/json":{"schema":{"type":"object","properties":{"organizations":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["organizations","pagination"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"}}}},"/admin/organizations/{id}":{"get":{"tags":["Admin"],"operationId":"adminGetOrganization","summary":"Get organization by ID","description":"Retrieve a single organization's full details including all members and subscription info. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Organization details with members and subscription","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{},"members":{"type":"array","items":{}},"subscription":{"type":["object","null"],"properties":{"id":{"type":"string"},"plan":{"type":"string"},"status":{"type":["string","null"]},"periodStart":{"type":["string","null"]},"periodEnd":{"type":["string","null"]},"cancelAtPeriodEnd":{"type":["boolean","null"]},"seats":{"type":["number","null"]},"trialStart":{"type":["string","null"]},"trialEnd":{"type":["string","null"]},"hasBillingCustomer":{"type":"boolean"}},"required":["id","plan","status","periodStart","periodEnd","cancelAtPeriodEnd","seats","trialStart","trialEnd","hasBillingCustomer"]}},"required":["members","subscription"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization not found"}}},"put":{"tags":["Admin"],"operationId":"adminUpdateOrganization","summary":"Update an organization","description":"Update organization fields (name, slug, type, contact info, etc.). Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"slug":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,48}[a-z0-9])?$"},"city":{"type":["string","null"],"maxLength":100},"email":{"type":["string","null"],"maxLength":255,"format":"email"},"phone":{"type":["string","null"],"maxLength":50},"address":{"type":["string","null"],"maxLength":500},"addressLine2":{"type":["string","null"],"maxLength":500},"postcode":{"type":["string","null"],"maxLength":20},"country":{"type":"string"},"state":{"type":["string","null"],"maxLength":100},"website":{"type":["string","null"],"maxLength":500,"format":"uri"},"companyNumber":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}]},"taxRegistrationNumber":{"type":["string","null"],"maxLength":50},"billingEmail":{"type":["string","null"],"maxLength":255,"format":"email"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated organization","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{}}}}}},"400":{"description":"No valid fields to update"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization not found"},"409":{"description":"Organization identity conflict"}}}},"/admin/organizations/{id}/subscription/checkout":{"post":{"tags":["Admin"],"operationId":"adminCreateOrganizationSubscriptionCheckout","summary":"Create organization subscription checkout session","description":"Create a Stripe Checkout session for a lab organization's subscription. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["base","growth","scale"]},"annual":{"type":"boolean"},"returnUrl":{"type":"string","format":"uri"}},"required":["plan"]}}}},"responses":{"200":{"description":"Checkout session URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"Invalid request or unsupported organization"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization not found"},"409":{"description":"Checkout is already in progress or completed"},"502":{"description":"Stripe returned an invalid response"},"503":{"description":"Stripe Checkout is temporarily unavailable"}}}},"/admin/organizations/{id}/subscription/billing-portal":{"post":{"tags":["Admin"],"operationId":"adminCreateOrganizationBillingPortal","summary":"Create organization billing portal session","description":"Create a Stripe billing portal session for a lab organization's subscription. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"Billing portal URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"Invalid request or unsupported organization"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization or billing customer not found"}}}},"/admin/organizations/{id}/subscription/provider-audit":{"get":{"tags":["Admin"],"operationId":"adminAuditOrganizationProviderSubscriptions","summary":"Audit Stripe subscriptions GuideLab does not track","description":"Reports live Stripe subscriptions for a lab organization's customer that have no local subscription row. Bounded to one indexed customer lookup and one subscription page. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Untracked provider subscriptions","content":{"application/json":{"schema":{"type":"object","properties":{"stripeCustomerId":{"type":["string","null"]},"truncated":{"type":"boolean"},"untracked":{"type":"array","items":{"type":"object","properties":{"stripeSubscriptionId":{"type":"string"},"status":{"type":"string"},"plan":{"type":["string","null"]},"billingInterval":{"type":["string","null"]},"billingCurrency":{"type":["string","null"]},"trialEnd":{"type":["string","null"]},"currentPeriodEnd":{"type":["string","null"]},"adoptable":{"type":"boolean"}},"required":["stripeSubscriptionId","status","plan","billingInterval","billingCurrency","trialEnd","currentPeriodEnd","adoptable"]}}},"required":["stripeCustomerId","truncated","untracked"]}}}},"400":{"description":"Unsupported organization"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization not found"},"500":{"description":"Internal server error"}}}},"/admin/organizations/{id}/subscription/provider-audit/adopt":{"post":{"tags":["Admin"],"operationId":"adminAdoptUntrackedProviderSubscription","summary":"Adopt an untracked Stripe subscription","description":"Publishes an untracked Stripe subscription as the lab organization's local subscription row so the duplicate-billing guards can see it. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stripeSubscriptionId":{"type":"string","minLength":1}},"required":["stripeSubscriptionId"]}}}},"responses":{"200":{"description":"Adopted subscription","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptionId":{"type":"string"}},"required":["subscriptionId"]}}}},"400":{"description":"Unsupported organization"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization or untracked subscription not found"},"409":{"description":"The subscription cannot be modelled by GuideLab"},"500":{"description":"Internal server error"}}}},"/admin/subscription-quarantines":{"get":{"tags":["Admin"],"operationId":"adminListSubscriptionQuarantines","summary":"List duplicate subscription compensations","description":"List recent unexpected Stripe Checkout completions and their cancellation/refund state. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Subscription quarantine records","content":{"application/json":{"schema":{"type":"object","properties":{"quarantines":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string"},"organizationId":{"type":"string"},"stripeCheckoutSessionId":{"type":"string"},"stripeSubscriptionId":{"type":"string"},"stripeInvoiceId":{"type":["string","null"]},"status":{"type":"string","enum":["pending","refunded","no_payment","needs_review"]},"refundIds":{"type":"array","items":{"type":"string"}},"errorCode":{"type":["string","null"]},"notifiedAt":{"type":["string","null"],"format":"date-time"},"resolvedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["eventId","organizationId","stripeCheckoutSessionId","stripeSubscriptionId","stripeInvoiceId","status","refundIds","errorCode","notifiedAt","resolvedAt","createdAt","updatedAt"]}}},"required":["quarantines"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"}}}},"/admin/subscription-quarantines/{eventId}/retry":{"post":{"tags":["Admin"],"operationId":"adminRetrySubscriptionQuarantine","summary":"Retry duplicate subscription compensation","description":"Retry the idempotent cancellation/refund workflow for an unexpected Stripe Checkout completion. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"eventId","in":"path"}],"responses":{"200":{"description":"Updated subscription quarantine","content":{"application/json":{"schema":{"type":"object","properties":{"quarantine":{"type":"object","properties":{"eventId":{"type":"string"},"organizationId":{"type":"string"},"stripeCheckoutSessionId":{"type":"string"},"stripeSubscriptionId":{"type":"string"},"stripeInvoiceId":{"type":["string","null"]},"status":{"type":"string","enum":["pending","refunded","no_payment","needs_review"]},"refundIds":{"type":"array","items":{"type":"string"}},"errorCode":{"type":["string","null"]},"notifiedAt":{"type":["string","null"],"format":"date-time"},"resolvedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["eventId","organizationId","stripeCheckoutSessionId","stripeSubscriptionId","stripeInvoiceId","status","refundIds","errorCode","notifiedAt","resolvedAt","createdAt","updatedAt"]}},"required":["quarantine"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Quarantine record not found"},"503":{"description":"Stripe compensation is temporarily unavailable"}}}},"/admin/organizations/{id}/members/{memberId}":{"delete":{"tags":["Admin"],"operationId":"adminRemoveOrganizationMember","summary":"Remove a member from an organization","description":"Remove a member from an organization by member ID. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"Member removed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Missing required IDs"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Member not found"}}}},"/admin/set-viewing-org":{"post":{"tags":["Admin"],"operationId":"adminSetViewingOrg","summary":"Set admin viewing organization","description":"Set a cookie to impersonate/view a specific organization's data. Used by platform admins to inspect organization dashboards. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string"}},"required":["organizationId"]}}}},"responses":{"200":{"description":"Viewing organization cookie set","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"organizationType":{"type":["string","null"]}},"required":["success","organizationType"]}}}},"400":{"description":"organizationId is required"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Organization not found"}}},"delete":{"tags":["Admin"],"operationId":"adminClearViewingOrg","summary":"Clear admin viewing organization","description":"Remove the admin viewing organization cookie, returning to the admin's own context. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Viewing organization cookie cleared","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"}}}},"/admin/notifications/preview":{"post":{"tags":["Admin"],"operationId":"adminPreviewNotification","summary":"Preview a notification template","description":"Preview how a notification will render across channels (email, SMS, WhatsApp). Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"eventType":{"type":"string","enum":["order.status_changed","order.assigned","order.task_assigned","order.message_added","order.phase_advance_requested","order.created","order.due_soon","order.overdue","order.files_reminder","order.hold_created","order.hold_file_submitted","order.hold_requirement_approved","order.hold_requirement_rejected","order.hold_resolved","order.surgical_report_sent","order.surgical_report_approved","order.surgical_report_changes_requested","partnership.requested","partnership.accepted","partnership.rejected","partnership.suspended","partnership.reactivated","partnership.terminated","invoice.sent","payment.received","payment.failed","payment.reconciliation_required","finance.late_fee_currency_unconfigured","org.join_request","org.join_request_approved","org.join_request_rejected","org.member_joined_via_code","inventory.low_stock"]},"channel":{"type":"string","enum":["email","sms","whatsapp"]},"data":{"type":"object","properties":{"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"entityUrl":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":"string"}},"required":["title","body"],"additionalProperties":{}}},"required":["eventType","channel","data"]}}}},"responses":{"200":{"description":"Rendered notification preview","content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"},"charCount":{"type":"integer"}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"}}}},"/admin/notifications/test":{"post":{"tags":["Admin"],"operationId":"adminTestNotification","summary":"Send a test notification","description":"Send a test notification through the selected delivery channel. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"eventType":{"type":"string","enum":["order.status_changed","order.assigned","order.task_assigned","order.message_added","order.phase_advance_requested","order.created","order.due_soon","order.overdue","order.files_reminder","order.hold_created","order.hold_file_submitted","order.hold_requirement_approved","order.hold_requirement_rejected","order.hold_resolved","order.surgical_report_sent","order.surgical_report_approved","order.surgical_report_changes_requested","partnership.requested","partnership.accepted","partnership.rejected","partnership.suspended","partnership.reactivated","partnership.terminated","invoice.sent","payment.received","payment.failed","payment.reconciliation_required","finance.late_fee_currency_unconfigured","org.join_request","org.join_request_approved","org.join_request_rejected","org.member_joined_via_code","inventory.low_stock"]},"channel":{"type":"string","enum":["email","sms","whatsapp"]},"data":{"type":"object","properties":{"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"entityUrl":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":"string"}},"required":["title","body"],"additionalProperties":{}},"recipient":{"type":"string","minLength":1}},"required":["eventType","channel","data","recipient"]}}}},"responses":{"200":{"description":"Notification accepted for delivery","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"429":{"description":"Paid test-notification rate limit exceeded"}}}},"/admin/privacy/organization-closures/{organizationId}/requeue":{"post":{"tags":["Admin"],"operationId":"adminRequeueOrganizationClosure","summary":"Requeue a failed organization closure","description":"Resets only a terminal failed closure job and writes an attributable intervention receipt. Replays against active or completed jobs are read-only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"organizationId","in":"path"}],"responses":{"200":{"description":"Closure requeue result","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["requeued","already_active","completed"]}},"required":["status"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Closure job not found"},"409":{"description":"Organization is no longer closing"}}}},"/admin/privacy/patient-erasures/{patientId}/requeue":{"post":{"tags":["Admin"],"operationId":"adminRequeuePatientErasure","summary":"Requeue a failed patient erasure","description":"Resets only a terminal failed patient-erasure job and writes an attributable intervention receipt. Replays against active or completed jobs are read-only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"patientId","in":"path"}],"responses":{"200":{"description":"Patient-erasure requeue result","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["requeued","already_active","completed"]}},"required":["status"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Patient-erasure job not found"}}}},"/admin/retention-holds":{"get":{"tags":["Admin"],"operationId":"adminListPlatformCustodyRetentionHolds","summary":"List platform-custody retention holds","description":"Lists a bounded set of holds transferred to platform legal custody during organization closure.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["live","released","all"],"default":"live"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"required":false,"name":"holdingOrganizationId","in":"query"}],"responses":{"200":{"description":"Platform-custody retention holds","content":{"application/json":{"schema":{"type":"object","properties":{"holds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"holdingOrganizationId":{"type":"string"},"matterKey":{"type":"string"},"operation":{"type":"string","enum":["all","identity_erasure","object_retirement","organization_closure","conversation_deletion"]},"reason":{"type":"string"},"scope":{"type":"object","properties":{"kind":{"type":"string","enum":["organization","partnership","patient","order","invoice","statement","file","conversation","conversation_attachment","scan_inbox_item"]},"id":{"type":"string","minLength":1,"maxLength":200}},"required":["kind","id"]},"placedAt":{"type":"string","format":"date-time"},"placedBy":{"type":"string"},"releasedAt":{"type":["string","null"],"format":"date-time"},"releasedBy":{"type":["string","null"]},"reviewDueAt":{"type":["string","null"],"format":"date-time"},"platformCustodyAt":{"type":["string","null"],"format":"date-time"}},"required":["id","holdingOrganizationId","matterKey","operation","reason","scope","placedAt","placedBy","releasedAt","releasedBy","reviewDueAt","platformCustodyAt"]}}},"required":["holds"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"500":{"description":"Internal server error"}}}},"/admin/retention-holds/{id}/release":{"post":{"tags":["Admin"],"operationId":"adminReleasePlatformCustodyRetentionHold","summary":"Release a platform-custody retention hold","description":"Releases a hold after its original organization has closed, preserving the original holder and evidence.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Retention hold released or already released","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["released","already_released"]}},"required":["status"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Retention hold not found"},"409":{"description":"Retention hold is not in platform legal custody"},"500":{"description":"Internal server error"}}}},"/admin/conversations/{id}/deletion-override":{"post":{"tags":["Admin"],"operationId":"adminOverrideConversationDeletion","summary":"Authorize whole-conversation deletion as platform legal","description":"Authorizes permanent placeholder conversion without participant approvals. Platform admin access and immutable database evidence are required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Deletion authorized or already authorized","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversationId":{"type":"string"},"status":{"type":"string","enum":["pending","authorized","completed"]},"authorizationKind":{"type":["string","null"]},"requestedAt":{"type":"string"},"authorizedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"approvedOrganizationIds":{"type":"array","items":{"type":"string"}}},"required":["conversationId","status","authorizationKind","requestedAt","authorizedAt","completedAt","approvedOrganizationIds"]}},"required":["data"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Conversation not found"},"409":{"description":"A correlated notification delivery is in flight"},"500":{"description":"Internal server error"}}}},"/admin/platform-billing-command-reviews":{"get":{"tags":["Admin"],"operationId":"adminListPlatformBillingCommandReviews","summary":"List platform billing commands requiring or retaining review","description":"Lists the bounded platform billing review queue and its immutable normalized resolution evidence. Platform admin access required.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["unresolved","resolved","all"],"default":"unresolved"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string","enum":["plan_change","storage_add_on"]},"required":false,"name":"kind","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Platform billing review queue","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"commandId":{"type":"string"},"organizationId":{"type":"string"},"subscriptionId":{"type":"string"},"kind":{"type":"string","enum":["plan_change","storage_add_on"]},"status":{"type":"string"},"errorCode":{"type":["string","null"]},"reviewRequiredAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"recoveryAttempts":{"type":"integer","minimum":0,"maximum":4},"targetPlan":{"type":["string","null"],"enum":["base","growth","scale",null]},"targetBillingInterval":{"type":["string","null"],"enum":["month","year",null]},"application":{"type":["string","null"],"enum":["immediate","scheduled",null]},"targetUnits":{"type":["integer","null"],"minimum":0,"maximum":1000},"baseUnits":{"type":["integer","null"],"minimum":0,"maximum":1000},"billingInterval":{"type":["string","null"],"enum":["month","year",null]},"change":{"type":["string","null"]},"resolution":{"type":["object","null"],"properties":{"outcome":{"type":"string","enum":["confirmed_provider_target","accepted_current_provider_state"]},"resultStatus":{"type":"string","enum":["completed","scheduled","failed"]},"reviewerUserId":{"type":"string"},"reason":{"type":"string"},"reviewRequiredAt":{"type":"string","format":"date-time"},"reviewedAt":{"type":"string","format":"date-time"},"sourceErrorCode":{"type":"string"},"providerReadAt":{"type":"string","format":"date-time"},"providerSubscriptionStatus":{"type":"string"},"providerScheduleId":{"type":["string","null"]},"activePlan":{"type":"string","enum":["base","growth","scale"]},"activeBillingInterval":{"type":"string","enum":["month","year"]},"activeStorageUnits":{"type":"integer","minimum":0,"maximum":1000},"scheduledPlan":{"type":["string","null"],"enum":["base","growth","scale",null]},"scheduledBillingInterval":{"type":["string","null"],"enum":["month","year",null]},"scheduledStorageUnits":{"type":"integer","minimum":0,"maximum":1000},"scheduledAt":{"type":["string","null"],"format":"date-time"}},"required":["outcome","resultStatus","reviewerUserId","reason","reviewRequiredAt","reviewedAt","sourceErrorCode","providerReadAt","providerSubscriptionStatus","providerScheduleId","activePlan","activeBillingInterval","activeStorageUnits","scheduledPlan","scheduledBillingInterval","scheduledStorageUnits","scheduledAt"]}},"required":["commandId","organizationId","subscriptionId","kind","status","errorCode","reviewRequiredAt","createdAt","recoveryAttempts","targetPlan","targetBillingInterval","application","targetUnits","baseUnits","billingInterval","change","resolution"]}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"500":{"description":"Internal server error"}}}},"/admin/platform-billing-command-reviews/{commandId}/resolve":{"post":{"tags":["Admin"],"operationId":"adminResolvePlatformBillingCommandReview","summary":"Resolve one ambiguous platform billing command","description":"Performs at most one Stripe subscription read and one optional schedule read, never writes to Stripe, and closes the command with immutable normalized evidence.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"commandId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string","enum":["confirmed_provider_target","accepted_current_provider_state"]},"reason":{"type":"string","minLength":1,"maxLength":500}},"required":["outcome","reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Review resolved or exact resolution replayed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"commandId":{"type":"string"},"organizationId":{"type":"string"},"subscriptionId":{"type":"string"},"kind":{"type":"string","enum":["plan_change","storage_add_on"]},"status":{"type":"string"},"errorCode":{"type":["string","null"]},"reviewRequiredAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"recoveryAttempts":{"type":"integer","minimum":0,"maximum":4},"targetPlan":{"type":["string","null"],"enum":["base","growth","scale",null]},"targetBillingInterval":{"type":["string","null"],"enum":["month","year",null]},"application":{"type":["string","null"],"enum":["immediate","scheduled",null]},"targetUnits":{"type":["integer","null"],"minimum":0,"maximum":1000},"baseUnits":{"type":["integer","null"],"minimum":0,"maximum":1000},"billingInterval":{"type":["string","null"],"enum":["month","year",null]},"change":{"type":["string","null"]},"resolution":{"type":["object","null"],"properties":{"outcome":{"type":"string","enum":["confirmed_provider_target","accepted_current_provider_state"]},"resultStatus":{"type":"string","enum":["completed","scheduled","failed"]},"reviewerUserId":{"type":"string"},"reason":{"type":"string"},"reviewRequiredAt":{"type":"string","format":"date-time"},"reviewedAt":{"type":"string","format":"date-time"},"sourceErrorCode":{"type":"string"},"providerReadAt":{"type":"string","format":"date-time"},"providerSubscriptionStatus":{"type":"string"},"providerScheduleId":{"type":["string","null"]},"activePlan":{"type":"string","enum":["base","growth","scale"]},"activeBillingInterval":{"type":"string","enum":["month","year"]},"activeStorageUnits":{"type":"integer","minimum":0,"maximum":1000},"scheduledPlan":{"type":["string","null"],"enum":["base","growth","scale",null]},"scheduledBillingInterval":{"type":["string","null"],"enum":["month","year",null]},"scheduledStorageUnits":{"type":"integer","minimum":0,"maximum":1000},"scheduledAt":{"type":["string","null"],"format":"date-time"}},"required":["outcome","resultStatus","reviewerUserId","reason","reviewRequiredAt","reviewedAt","sourceErrorCode","providerReadAt","providerSubscriptionStatus","providerScheduleId","activePlan","activeBillingInterval","activeStorageUnits","scheduledPlan","scheduledBillingInterval","scheduledStorageUnits","scheduledAt"]}},"required":["commandId","organizationId","subscriptionId","kind","status","errorCode","reviewRequiredAt","createdAt","recoveryAttempts","targetPlan","targetBillingInterval","application","targetUnits","baseUnits","billingInterval","change","resolution"]},"replayed":{"type":"boolean"}},"required":["data","replayed"]}}}},"400":{"description":"Invalid review request or idempotency header"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - platform admin only"},"404":{"description":"Billing review not found"},"409":{"description":"Review is in progress, already resolved, idempotency-conflicted, or provider state does not satisfy the chosen outcome"},"429":{"description":"Per-command provider-read budget or backoff reached"},"500":{"description":"Stripe catalog configuration unavailable"},"503":{"description":"Provider state could not be read"}}}},"/conversations":{"get":{"tags":["Conversations"],"operationId":"listConversations","summary":"List conversations","description":"Retrieve paginated conversations for the current organization. Supports filtering by all/unread/waiting/responded and text search across partner names and message previews. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["all","unread","waiting","responded"],"example":"all"},"required":false,"name":"filter","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"0"},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Paginated list of conversations with unread counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":"string"},"orderId":{"type":["string","null"]},"title":{"type":["string","null"]},"lastMessageId":{"type":["string","null"]},"lastMessageAt":{"type":["string","null"]},"lastMessagePreview":{"type":["string","null"]},"lastMessageSenderId":{"type":["string","null"]},"unreadCounts":{"type":["object","null"],"additionalProperties":{"type":"number"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"partner":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"nameMissing":{"type":"boolean"},"logo":{"type":["string","null"]}},"required":["id","name","nameMissing","logo"]},"order":{"type":["object","null"],"properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string"},"patientId":{"type":["string","null"]},"hidePatientFromLab":{"type":"boolean"}},"required":["id","orderNumber","status","patientId","hidePatientFromLab"]},"patient":{"type":["object","null"],"properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"patientCode":{"type":["string","null"]}},"required":["id","firstName","lastName","patientCode"]},"lastMessageSender":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"image":{"type":["string","null"]}},"required":["id","name","image"]}},"required":["id","partnershipId","orderId","title","lastMessageId","lastMessageAt","lastMessagePreview","lastMessageSenderId","unreadCounts","createdAt","updatedAt","partner","order","patient","lastMessageSender"]}},"totalUnread":{"type":"number"},"pagination":{"type":"object","properties":{"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"hasMore":{"type":"boolean"}},"required":["total","limit","offset","hasMore"]}},"required":["data","totalUnread","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"}}},"post":{"tags":["Conversations"],"operationId":"createConversation","summary":"Create a conversation","description":"Create a new conversation within a partnership. Optionally link it to an order and include an initial message. If a conversation already exists for the same partnership+order, returns the existing one. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"partnershipId":{"type":"string","minLength":1},"orderId":{"type":"string"},"title":{"type":"string"},"initialMessage":{"type":"string","minLength":1},"initialMessageId":{"type":"string","format":"uuid"}},"required":["partnershipId"]}}}},"responses":{"200":{"description":"Existing conversation returned","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":"string"},"orderId":{"type":["string","null"]},"title":{"type":["string","null"]},"lastMessageId":{"type":["string","null"]},"lastMessageAt":{"type":["string","null"]},"lastMessagePreview":{"type":["string","null"]},"lastMessageSenderId":{"type":["string","null"]},"unreadCounts":{"type":["object","null"],"additionalProperties":{"type":"number"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","partnershipId","orderId","title","lastMessageId","lastMessageAt","lastMessagePreview","lastMessageSenderId","unreadCounts","createdAt","updatedAt"]},"existing":{"type":"boolean"}},"required":["data","existing"]}}}},"201":{"description":"Conversation created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":"string"},"orderId":{"type":["string","null"]},"title":{"type":["string","null"]},"lastMessageId":{"type":["string","null"]},"lastMessageAt":{"type":["string","null"]},"lastMessagePreview":{"type":["string","null"]},"lastMessageSenderId":{"type":["string","null"]},"unreadCounts":{"type":["object","null"],"additionalProperties":{"type":"number"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","partnershipId","orderId","title","lastMessageId","lastMessageAt","lastMessagePreview","lastMessageSenderId","unreadCounts","createdAt","updatedAt"]},"existing":{"type":"boolean"}},"required":["data","existing"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this partnership"},"404":{"description":"Partnership not found"},"409":{"description":"Conversation or initial-message idempotency conflict"}}}},"/conversations/{id}":{"get":{"tags":["Conversations"],"operationId":"getConversation","summary":"Get conversation by ID","description":"Retrieve a single conversation with its partner organization, linked order, and last message sender details. Only accessible by organizations that are part of the conversation's partnership.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Conversation details with partner and order info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":"string"},"orderId":{"type":["string","null"]},"title":{"type":["string","null"]},"lastMessageId":{"type":["string","null"]},"lastMessageAt":{"type":["string","null"]},"lastMessagePreview":{"type":["string","null"]},"lastMessageSenderId":{"type":["string","null"]},"unreadCounts":{"type":["object","null"],"additionalProperties":{"type":"number"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"partner":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"nameMissing":{"type":"boolean"},"logo":{"type":["string","null"]}},"required":["id","name","nameMissing","logo"]},"order":{"type":["object","null"],"properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string"}},"required":["id","orderNumber","status"]},"lastMessageSender":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"image":{"type":["string","null"]}},"required":["id","name","image"]},"draftContext":{"type":["object","null"],"properties":{"clinicName":{"type":"string"},"clinicNameMissing":{"type":"boolean"},"doctorName":{"type":["string","null"]},"patientInitials":{"type":"string"},"targetDeliveryDate":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"productName":{"type":"string"},"materialName":{"type":["string","null"]},"quantity":{"type":"number"},"teethCount":{"type":"number"}},"required":["id","productName","materialName","quantity","teethCount"]}}},"required":["clinicName","clinicNameMissing","doctorName","patientInitials","targetDeliveryDate","items"]}},"required":["id","partnershipId","orderId","title","lastMessageId","lastMessageAt","lastMessagePreview","lastMessageSenderId","unreadCounts","createdAt","updatedAt","partner","order","lastMessageSender","draftContext"]}},"required":["data"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation not found"}}}},"/conversations/{id}/messages":{"get":{"tags":["Conversations"],"operationId":"listConversationMessages","summary":"List messages in a conversation","description":"Retrieve paginated messages for a conversation with sender info, org details, and attachments. Internal notes are only visible to the org that sent them. Supports cursor-based pagination via before/after message IDs.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"before","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"after","in":"query"}],"responses":{"200":{"description":"Paginated list of messages with sender and attachment details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"conversationId":{"type":"string"},"partnershipId":{"type":"string"},"orderId":{"type":["string","null"]},"senderUserId":{"type":"string"},"senderOrgId":{"type":"string"},"content":{"type":"string"},"messageType":{"type":"string"},"sequence":{"type":"number"},"isInternal":{"type":"boolean"},"replyToId":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"sender":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"nameMissing":{"type":"boolean"},"image":{"type":["string","null"]}},"required":["id","name","nameMissing","image"]},"senderOrg":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"nameMissing":{"type":"boolean"},"organizationType":{"type":"string"}},"required":["id","name","nameMissing","organizationType"]},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"messageId":{"type":"string"},"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"duration":{"type":["number","null"]},"hasThumbnail":{"type":"boolean"},"transcript":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","messageId","name","mimeType","size","duration","hasThumbnail","transcript","createdAt"]}},"replyTo":{}},"required":["id","conversationId","partnershipId","orderId","senderUserId","senderOrgId","content","messageType","sequence","isInternal","replyToId","createdAt","updatedAt","sender","senderOrg","attachments"]}},"readState":{"type":"object","properties":{"partnerOrganizationId":{"type":"string"},"partnerLastReadSequence":{"type":"number"}},"required":["partnerOrganizationId","partnerLastReadSequence"]},"pagination":{"type":"object","properties":{"hasMore":{"type":"boolean"},"oldestId":{"type":"string"},"newestId":{"type":"string"}},"required":["hasMore"]}},"required":["data","readState","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation not found"}}},"post":{"tags":["Conversations"],"operationId":"sendMessage","summary":"Send a message in a conversation","description":"Send a text, image, voice, or file message in a conversation. The client-generated message ID makes exact retries idempotent within the conversation and sender scope. Supports attachments, internal notes (visible only to your org), and reply threads. Updates unread counts and broadcasts to connected WebSocket clients.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clientMessageId":{"type":"string","format":"uuid"},"content":{"type":"string","maxLength":10000,"default":""},"messageType":{"type":"string","enum":["text","image","voice","file"],"default":"text"},"isInternal":{"type":"boolean","default":false},"replyToId":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string","minLength":1},"duration":{"type":"integer","minimum":0,"maximum":86400}},"required":["attachmentId"],"additionalProperties":false},"maxItems":20}},"required":["clientMessageId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Message sent with sender and attachment details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"conversationId":{"type":"string"},"partnershipId":{"type":"string"},"orderId":{"type":["string","null"]},"senderUserId":{"type":"string"},"senderOrgId":{"type":"string"},"content":{"type":"string"},"messageType":{"type":"string"},"isInternal":{"type":"boolean"},"replyToId":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"sender":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"nameMissing":{"type":"boolean"},"image":{"type":["string","null"]}},"required":["id","name","nameMissing","image"]},"senderOrg":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"nameMissing":{"type":"boolean"},"organizationType":{"type":"string"}},"required":["id","name","nameMissing","organizationType"]},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"messageId":{"type":"string"},"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"duration":{"type":["number","null"]},"hasThumbnail":{"type":"boolean"},"transcript":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","messageId","name","mimeType","size","duration","hasThumbnail","transcript","createdAt"]}},"replyTo":{}},"required":["id","conversationId","partnershipId","orderId","senderUserId","senderOrgId","content","messageType","isInternal","replyToId","createdAt","updatedAt","sender","senderOrg","attachments"]}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation not found"},"409":{"description":"Client message ID is already in use"},"422":{"description":"Client message ID was reused with different data"}}}},"/conversations/{id}/read":{"post":{"tags":["Conversations"],"operationId":"markConversationRead","summary":"Mark conversation as read","description":"Advance the current organization's conversation read cursor to the latest committed message sequence. Broadcasts the organization-wide cursor to connected clients.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Conversation marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"lastReadSequence":{"type":"number"}},"required":["success","lastReadSequence"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation not found"}}}},"/conversations/{id}/deletion-approval":{"post":{"tags":["Conversations"],"operationId":"approveConversationDeletion","summary":"Approve whole-conversation deletion","description":"Records the current participant organization's immutable approval. Deletion is authorized only after both the lab and clinic approve; platform legal has a separate admin override.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Deletion approval recorded or deletion already authorized","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversationId":{"type":"string"},"status":{"type":"string","enum":["pending","authorized","completed"]},"authorizationKind":{"type":["string","null"]},"requestedAt":{"type":"string"},"authorizedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"approvedOrganizationIds":{"type":"array","items":{"type":"string"}}},"required":["conversationId","status","authorizationKind","requestedAt","authorizedAt","completedAt","approvedOrganizationIds"]}},"required":["data"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Current organization is not a participant"},"404":{"description":"Conversation not found"},"409":{"description":"A correlated notification delivery is in flight"}}}},"/conversations/{id}/attachments/{attachmentId}":{"delete":{"tags":["Conversations"],"operationId":"archiveConversationAttachment","summary":"Archive a participant-owned conversation attachment","description":"Immediately replaces an attachment owned by the current participant with a permanent placeholder. Its objects enter the shared hold-aware retirement lifecycle after 90 days.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"attachmentId","in":"path"}],"responses":{"200":{"description":"Attachment archived or already archived","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","enum":["[Attachment archived]"]},"mimeType":{"type":"string","enum":["application/octet-stream"]},"size":{"type":"number","enum":[0]},"duration":{"type":"null"},"transcript":{"type":"null"},"hasThumbnail":{"type":"boolean","enum":[false]}},"required":["id","name","mimeType","size","duration","transcript","hasThumbnail"]}},"required":["data"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Attachment belongs to the other participant"},"404":{"description":"Conversation attachment not found"},"409":{"description":"Attachment has not been finalized"}}}},"/conversations/{id}/attachments/presign":{"post":{"tags":["Conversations"],"operationId":"presignConversationAttachment","summary":"Prepare a direct conversation attachment upload","description":"Creates one-use, tenant-bound staging keys and exact-size/content-type presigned PUT URLs. Call finalize after both uploads complete.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string","minLength":1,"maxLength":255},"mimeType":{"type":"string","minLength":1,"maxLength":255},"size":{"type":"integer","minimum":1,"maximum":52428800},"thumbnailSize":{"type":"integer","minimum":1,"maximum":2097152}},"required":["filename","mimeType","size"]}}}},"responses":{"201":{"description":"One-use attachment upload claim created","content":{"application/json":{"schema":{"type":"object","properties":{"attachmentId":{"type":"string"},"uploadUrl":{"type":"string"},"uploadContentType":{"type":"string"},"thumbnailUploadUrl":{"type":["string","null"]},"thumbnailUploadContentType":{"type":["string","null"]},"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"category":{"type":"string","enum":["images","voice","files"]}},"required":["attachmentId","uploadUrl","uploadContentType","thumbnailUploadUrl","thumbnailUploadContentType","name","mimeType","size","category"]}}}},"400":{"description":"Invalid attachment metadata"},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation not found"},"429":{"description":"Upload issuance budget exceeded"}}}},"/conversations/{id}/attachments/finalize":{"post":{"tags":["Conversations"],"operationId":"finalizeConversationAttachment","summary":"Finalize a direct conversation attachment upload","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attachmentId":{"type":"string","minLength":1}},"required":["attachmentId"]}}}},"responses":{"200":{"description":"Attachment is ready to send"},"400":{"description":"Uploaded object does not match the claim"},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation or upload claim not found"},"409":{"description":"Upload claim cannot currently be finalized"}}}},"/conversations/{id}/attachments/transcribe":{"post":{"tags":["Conversations"],"operationId":"transcribeConversationAttachment","summary":"Transcribe a voice attachment","description":"Transcribe an audio attachment using OpenAI Whisper. The attachment may be a ready unsent upload claim owned by the current user or an attachment already sent in the conversation.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attachmentId":{"type":"string","minLength":1},"dataProcessingConsent":{"type":"boolean","enum":[true]}},"required":["attachmentId","dataProcessingConsent"]}}}},"responses":{"200":{"description":"Transcription result","content":{"application/json":{"schema":{"type":"object","properties":{"transcript":{"type":"string"}},"required":["transcript"]}}}},"400":{"description":"Invalid request or attachment is not audio"},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized for this conversation"},"404":{"description":"Conversation or attachment not found"},"409":{"description":"Attachment transcription is already in progress"},"429":{"description":"Transcription rate limit exceeded"}}}},"/conversations/attachments/{id}":{"get":{"tags":["Conversations"],"operationId":"getConversationAttachment","summary":"Download a conversation attachment","description":"Stream a conversation attachment file from R2 storage. Supports thumbnail mode via ?thumbnail=true and proxy mode via ?proxy=true. Only accessible by organizations that are part of the conversation's partnership.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Set to 'true' to return thumbnail version","example":"false"},"required":false,"description":"Set to 'true' to return thumbnail version","name":"thumbnail","in":"query"},{"schema":{"type":"string","description":"Set to 'true' to serve inline instead of as download","example":"false"},"required":false,"description":"Set to 'true' to serve inline instead of as download","name":"proxy","in":"query"}],"responses":{"200":{"description":"Attachment file stream","content":{"application/octet-stream":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Not authorized"},"404":{"description":"Attachment or file not found"}}}},"/notifications/unsubscribe":{"post":{"tags":["Notifications"],"operationId":"unsubscribeNotificationEmails","summary":"One-click unsubscribe from notification emails","description":"Public endpoint used by List-Unsubscribe headers to disable email notifications for a single notification category.","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"token","in":"query"}],"responses":{"200":{"description":"Notification email preference updated"},"400":{"description":"Invalid unsubscribe token"}}}},"/notifications":{"get":{"tags":["Notifications"],"operationId":"listNotifications","summary":"List notifications","description":"Retrieve paginated notifications for the current user within their active organization. Supports cursor-based pagination and filtering by unread status. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Max items to return (1-50, default 20)","example":"20"},"required":false,"description":"Max items to return (1-50, default 20)","name":"limit","in":"query"},{"schema":{"type":"string","description":"Cursor: notification ID to paginate before","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"description":"Cursor: notification ID to paginate before","name":"before","in":"query"},{"schema":{"type":"string","description":"Set to 'true' to return only unread notifications","example":"false"},"required":false,"description":"Set to 'true' to return only unread notifications","name":"unreadOnly","in":"query"}],"responses":{"200":{"description":"Paginated list of notifications with unread count","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"organizationId":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"data":{},"readAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","userId","organizationId","type","title","body","readAt","createdAt"]}},"unreadCount":{"type":"number"},"hasMore":{"type":"boolean"}},"required":["data","unreadCount","hasMore"]}}}},"401":{"description":"Unauthorized"}}}},"/notifications/count":{"get":{"tags":["Notifications"],"operationId":"getConversationUnreadCount","summary":"Get conversation unread count","description":"Returns the total number of unread conversation messages across all partnerships for the current organization. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Total unread conversation message count","content":{"application/json":{"schema":{"type":"object","properties":{"unreadCount":{"type":"number"}},"required":["unreadCount"]}}}},"401":{"description":"Unauthorized"}}}},"/notifications/mark-read":{"post":{"tags":["Notifications"],"operationId":"markNotificationsRead","summary":"Mark notifications as read","description":"Mark specific notifications or all unread notifications as read for the current user. Provide either an array of notificationIds or set all to true. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"notificationIds":{"type":"array","items":{"type":"string"},"minItems":1},"all":{"type":"boolean","enum":[true]}}}}}},"responses":{"200":{"description":"Notifications marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"}}}},"/notifications/unread-count":{"get":{"tags":["Notifications"],"operationId":"getNotificationUnreadCount","summary":"Get notification unread count","description":"Returns the count of unread notifications for the current user within their active organization. Available to both labs and clinics.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Unread notification count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]}}}},"401":{"description":"Unauthorized"}}}},"/push-subscriptions/vapid-key":{"get":{"tags":["Push Subscriptions"],"operationId":"getVapidPublicKey","summary":"Get VAPID public key","description":"Returns the VAPID public key needed to subscribe to push notifications. Requires user authentication but no specific organization context.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"VAPID public key for push notification subscription","content":{"application/json":{"schema":{"type":"object","properties":{"publicKey":{"type":"string"}},"required":["publicKey"]}}}},"401":{"description":"Unauthorized"},"503":{"description":"Push notifications not configured on the server"}}}},"/push-subscriptions":{"post":{"tags":["Push Subscriptions"],"operationId":"registerPushSubscription","summary":"Register push subscription","description":"Register a new Web Push subscription for the current user. If the endpoint already exists, it will be updated with the new keys. Requires user authentication.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri"},"keys":{"type":"object","properties":{"p256dh":{"type":"string","minLength":1},"auth":{"type":"string","minLength":1}},"required":["p256dh","auth"]}},"required":["endpoint","keys"]}}}},"responses":{"201":{"description":"Push subscription registered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid subscription data"},"401":{"description":"Unauthorized"},"409":{"description":"Push subscription limit reached"}}},"get":{"tags":["Push Subscriptions"],"operationId":"listPushSubscriptions","summary":"List push subscriptions","description":"List all registered push subscriptions for the current user. Requires user authentication.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of registered push subscriptions for the current user","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"endpoint":{"type":"string"},"userAgent":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","endpoint","userAgent","createdAt"]}}},"required":["subscriptions"]}}}},"401":{"description":"Unauthorized"}}},"delete":{"tags":["Push Subscriptions"],"operationId":"unregisterPushSubscription","summary":"Unregister push subscription","description":"Remove a push subscription by its endpoint URL. Only subscriptions belonging to the current user will be removed. Requires user authentication.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri"}},"required":["endpoint"]}}}},"responses":{"200":{"description":"Push subscription removed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"}}}},"/company-settings":{"get":{"operationId":"getCompanySettings","description":"Retrieve the company settings for the current organization, including contact details, address, and preferences.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Company settings for the current organization","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"logo":{"type":["string","null"]},"labReference":{"type":["string","null"]},"email":{"type":["string","null"]},"billingEmail":{"type":["string","null"]},"address":{"type":["string","null"]},"addressLine2":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"country":{"type":["string","null"]},"postcode":{"type":["string","null"]},"timezone":{"type":"string"},"currency":{"type":"string"},"dateFormat":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"companyNumber":{"type":["string","null"]},"taxRegistrationNumber":{"type":["string","null"]},"teethChartingSystem":{"type":["string","null"]},"workTypeLayout":{"type":["string","null"]},"latitude":{"type":["string","null"]},"longitude":{"type":["string","null"]},"isDiscoverable":{"type":["boolean","null"]},"description":{"type":["string","null"]},"coverImage":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","slug","logo","labReference","email","billingEmail","address","addressLine2","city","state","country","postcode","timezone","currency","dateFormat","phone","website","companyNumber","taxRegistrationNumber","teethChartingSystem","workTypeLayout","latitude","longitude","isDiscoverable","description","coverImage","createdAt","updatedAt"]}},"required":["settings"]}}}},"404":{"description":"Organization not found"},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateCompanySettings","description":"Update company settings such as name, contact information, address, currency, and other organization preferences.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"labReference":{"type":["string","null"],"maxLength":100},"email":{"type":["string","null"],"maxLength":255,"format":"email"},"billingEmail":{"type":["string","null"],"maxLength":255,"format":"email"},"address":{"type":["string","null"],"maxLength":500},"addressLine2":{"type":["string","null"],"maxLength":500},"city":{"type":["string","null"],"maxLength":100},"state":{"type":["string","null"],"maxLength":100},"country":{"type":"string"},"postcode":{"type":["string","null"],"maxLength":20},"timezone":{"type":"string","minLength":1,"maxLength":100},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"acknowledgeCatalogRepricingRows":{"type":"integer","minimum":0},"dateFormat":{"type":["string","null"],"enum":["DD/MM/YYYY","MM/DD/YYYY","YYYY-MM-DD","DD-MM-YYYY","DD.MM.YYYY",null]},"phone":{"type":["string","null"],"maxLength":50},"website":{"anyOf":[{"type":["string","null"],"maxLength":500,"format":"uri"},{"type":"string","enum":[""]},{"type":"null"}]},"companyNumber":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}]},"taxRegistrationNumber":{"type":["string","null"],"maxLength":50},"teethChartingSystem":{"type":["string","null"],"enum":["FDI","Universal","Palmer",null]},"workTypeLayout":{"type":"string","enum":["standard","stepper"]},"latitude":{"type":["number","null"],"minimum":-90,"maximum":90},"longitude":{"type":["number","null"],"minimum":-180,"maximum":180},"isDiscoverable":{"type":"boolean"},"description":{"type":["string","null"],"maxLength":2000}},"additionalProperties":false}}}},"responses":{"200":{"description":"Successfully updated company settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"logo":{"type":["string","null"]},"labReference":{"type":["string","null"]},"email":{"type":["string","null"]},"billingEmail":{"type":["string","null"]},"address":{"type":["string","null"]},"addressLine2":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"country":{"type":["string","null"]},"postcode":{"type":["string","null"]},"timezone":{"type":"string"},"currency":{"type":"string"},"dateFormat":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"companyNumber":{"type":["string","null"]},"taxRegistrationNumber":{"type":["string","null"]},"teethChartingSystem":{"type":["string","null"]},"workTypeLayout":{"type":["string","null"]},"latitude":{"type":["string","null"]},"longitude":{"type":["string","null"]},"isDiscoverable":{"type":["boolean","null"]},"description":{"type":["string","null"]},"coverImage":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","slug","logo","labReference","email","billingEmail","address","addressLine2","city","state","country","postcode","timezone","currency","dateFormat","phone","website","companyNumber","taxRegistrationNumber","teethChartingSystem","workTypeLayout","latitude","longitude","isDiscoverable","description","coverImage","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Organization not found"},"409":{"description":"Currency conflicts with persisted catalog prices or the legal identity is already registered"},"500":{"description":"Internal server error"}}}},"/company-settings/logo":{"get":{"operationId":"getCompanyLogo","description":"Serve the company logo image from R2 storage. The optional orgId parameter is limited to the current organization, discoverable organizations, or active partners.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"orgId","in":"query"}],"responses":{"200":{"description":"Company logo image binary"},"404":{"description":"Organization or logo not found"},"500":{"description":"Internal server error"}}},"post":{"operationId":"uploadCompanyLogo","description":"Upload a new company logo image. Replaces any existing logo. Accepts JPEG, PNG, WebP, or GIF up to 5MB.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Successfully uploaded company logo","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"logo":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","logo","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid file type or file too large"},"404":{"description":"Organization not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteCompanyLogo","description":"Remove the company logo from R2 storage and clear the logo reference in the organization record.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Company logo successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"logo":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","logo","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["settings","message"]}}}},"400":{"description":"No logo to delete"},"404":{"description":"Organization not found"},"500":{"description":"Internal server error"}}}},"/company-settings/cover-image":{"get":{"operationId":"getCompanyCoverImage","description":"Serve the company cover image from R2 storage. The optional orgId parameter is limited to the current organization, discoverable organizations, or active partners.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"orgId","in":"query"}],"responses":{"200":{"description":"Cover image binary"},"404":{"description":"Organization or cover image not found"},"500":{"description":"Internal server error"}}},"post":{"operationId":"uploadCompanyCoverImage","description":"Upload a new cover image for the company profile. Replaces any existing cover image. Accepts JPEG, PNG, WebP, or GIF up to 5MB.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Cover image successfully uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid file type or file too large"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteCompanyCoverImage","description":"Remove the company cover image from R2 storage and clear the reference in the organization record.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Cover image successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"No cover image to delete"},"500":{"description":"Internal server error"}}}},"/company-settings/gallery":{"get":{"operationId":"listCompanyGalleryImages","description":"List all gallery images for the organization, sorted by display order. The optional orgId parameter is limited to the current organization, discoverable organizations, or active partners.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"orgId","in":"query"}],"responses":{"200":{"description":"List of gallery images with URLs and sort order","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"sortOrder":{"type":"number"}},"required":["id","url","sortOrder"]}}}}},"500":{"description":"Internal server error"}}},"post":{"operationId":"uploadCompanyGalleryImage","description":"Upload a new gallery image for the company profile. Accepts JPEG, PNG, WebP, or GIF up to 5MB. Automatically assigned the next sort order.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Successfully uploaded gallery image with ID and URL","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"sortOrder":{"type":"number"}},"required":["id","url","sortOrder"]}}}},"400":{"description":"Invalid file type or file too large"},"409":{"description":"Organization gallery image limit reached"},"500":{"description":"Internal server error"}}}},"/company-settings/gallery/reorder":{"post":{"operationId":"reorderCompanyGalleryImages","description":"Reorder gallery images by providing an ordered array of image IDs. Each image's sort order is updated to match its position in the array.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"minItems":1,"maxItems":12}},"required":["ids"],"additionalProperties":false}}}},"responses":{"200":{"description":"Gallery images successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/company-settings/gallery/{id}/image":{"get":{"operationId":"getCompanyGalleryImage","description":"Serve a specific gallery image binary from R2 storage by its ID.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Gallery image binary"},"404":{"description":"Gallery image not found"},"500":{"description":"Internal server error"}}}},"/company-settings/gallery/{id}":{"delete":{"operationId":"deleteCompanyGalleryImage","description":"Delete a specific gallery image from R2 storage and remove its database record.","tags":["Company Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Gallery image successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Gallery image not found"},"500":{"description":"Internal server error"}}}},"/document-settings":{"get":{"operationId":"getDocumentSettings","description":"Retrieve document settings for the current lab, including title, terms and conditions, footer text, and manufacture details.","tags":["Document Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Document settings for the current lab","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"documentType":{"type":"string"},"title":{"type":["string","null"]},"termsAndConditions":{"type":["string","null"]},"footerText":{"type":["string","null"]},"footerLogoKey":{"type":["string","null"]},"manufactureTitle":{"type":["string","null"]},"manufactureText":{"type":["string","null"]},"showAdditionalProductInfo":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","documentType","title","termsAndConditions","footerText","footerLogoKey","manufactureTitle","manufactureText","showAdditionalProductInfo","createdAt","updatedAt"]}},"required":["settings"]}}}},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateDocumentSettings","description":"Update document settings for the current lab. Creates default settings if none exist. Controls document title, terms, footer text, and manufacture info.","tags":["Document Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":["string","null"],"maxLength":200},"termsAndConditions":{"type":["string","null"],"maxLength":5000},"footerText":{"type":["string","null"],"maxLength":1000},"manufactureTitle":{"type":["string","null"],"maxLength":200},"manufactureText":{"type":["string","null"],"maxLength":2000},"showAdditionalProductInfo":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Successfully updated document settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"documentType":{"type":"string"},"title":{"type":["string","null"]},"termsAndConditions":{"type":["string","null"]},"footerText":{"type":["string","null"]},"footerLogoKey":{"type":["string","null"]},"manufactureTitle":{"type":["string","null"]},"manufactureText":{"type":["string","null"]},"showAdditionalProductInfo":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","documentType","title","termsAndConditions","footerText","footerLogoKey","manufactureTitle","manufactureText","showAdditionalProductInfo","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/document-settings/logo":{"get":{"operationId":"getDocumentFooterLogo","description":"Serve the document footer logo image from R2 storage for the current lab.","tags":["Document Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Footer logo image binary"},"404":{"description":"Document settings or footer logo not found"},"500":{"description":"Internal server error"}}},"post":{"operationId":"uploadDocumentFooterLogo","description":"Upload a new footer logo for documents. Replaces any existing footer logo. Accepts JPEG, PNG, WebP, or GIF up to 5MB.","tags":["Document Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Successfully uploaded footer logo with updated settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"documentType":{"type":"string"},"title":{"type":["string","null"]},"termsAndConditions":{"type":["string","null"]},"footerText":{"type":["string","null"]},"footerLogoKey":{"type":["string","null"]},"manufactureTitle":{"type":["string","null"]},"manufactureText":{"type":["string","null"]},"showAdditionalProductInfo":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","documentType","title","termsAndConditions","footerText","footerLogoKey","manufactureTitle","manufactureText","showAdditionalProductInfo","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid file type or file too large"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteDocumentFooterLogo","description":"Remove the document footer logo from R2 storage and clear the reference in the document settings.","tags":["Document Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Footer logo successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"documentType":{"type":"string"},"title":{"type":["string","null"]},"termsAndConditions":{"type":["string","null"]},"footerText":{"type":["string","null"]},"footerLogoKey":{"type":["string","null"]},"manufactureTitle":{"type":["string","null"]},"manufactureText":{"type":["string","null"]},"showAdditionalProductInfo":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","documentType","title","termsAndConditions","footerText","footerLogoKey","manufactureTitle","manufactureText","showAdditionalProductInfo","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["settings","message"]}}}},"400":{"description":"No logo to delete"},"404":{"description":"Document settings not found"},"500":{"description":"Internal server error"}}}},"/hold-reasons":{"get":{"operationId":"listHoldReasons","description":"List all hold reasons for the current lab, sorted by display order. Optionally include inactive reasons.","tags":["Hold Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of hold reasons for the lab","content":{"application/json":{"schema":{"type":"object","properties":{"reasons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"requiresFileReupload":{"type":"boolean"},"autoFillNotes":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","requiresFileReupload","autoFillNotes","createdAt","updatedAt"]}},"truncated":{"type":"boolean","description":"True when the lab holds more reasons than one response returns. The list is capped, not refused."}},"required":["reasons","truncated"]}}}},"400":{"description":"Invalid query parameters"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createHoldReason","description":"Create a new hold reason for the lab. Automatically assigns the next sort order if not specified.","tags":["Hold Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sortOrder":{"type":"integer","minimum":0},"requiresFileReupload":{"type":"boolean"},"autoFillNotes":{"type":["string","null"],"maxLength":1000}},"required":["name"]}}}},"responses":{"201":{"description":"Newly created hold reason","content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"requiresFileReupload":{"type":"boolean"},"autoFillNotes":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","requiresFileReupload","autoFillNotes","createdAt","updatedAt"]}},"required":["reason"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Lab not found"},"409":{"description":"Hold reason row limit reached"},"500":{"description":"Internal server error"}}}},"/hold-reasons/reorder":{"post":{"operationId":"reorderHoldReasons","description":"Reorder hold reasons by providing an ordered array of reason IDs. Each reason's sort order is updated to match its position.","tags":["Hold Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reasonIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["reasonIds"]}}}},"responses":{"200":{"description":"Hold reasons successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"One or more hold reasons not found"},"500":{"description":"Internal server error"}}}},"/hold-reasons/{id}":{"put":{"operationId":"updateHoldReason","description":"Update an existing hold reason's name, active status, file reupload requirement, or auto-fill notes.","tags":["Hold Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Hold reason ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Hold reason ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"isActive":{"type":"boolean"},"requiresFileReupload":{"type":"boolean"},"autoFillNotes":{"type":["string","null"],"maxLength":1000}}}}}},"responses":{"200":{"description":"Successfully updated hold reason","content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"requiresFileReupload":{"type":"boolean"},"autoFillNotes":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","requiresFileReupload","autoFillNotes","createdAt","updatedAt"]}},"required":["reason"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Hold reason not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteHoldReason","description":"Soft-delete a hold reason by marking it as inactive. The reason remains in the database for historical reference.","tags":["Hold Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Hold reason ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Hold reason ID","name":"id","in":"path"}],"responses":{"200":{"description":"Hold reason deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"requiresFileReupload":{"type":"boolean"},"autoFillNotes":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","requiresFileReupload","autoFillNotes","createdAt","updatedAt"]}},"required":["reason"]}}}},"404":{"description":"Hold reason not found"},"500":{"description":"Internal server error"}}}},"/qc-checklist":{"get":{"operationId":"listQcChecklistItems","description":"List all quality control checklist items for the current lab, sorted by display order. Optionally include inactive items.","tags":["QC Checklist"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":["boolean","null"],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of QC checklist items for the lab","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["items"]}}}},"400":{"description":"Invalid query parameters"},"409":{"description":"QC checklist row limit exceeded"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createQcChecklistItem","description":"Create a new quality control checklist item for the lab. Automatically assigns the next sort order if not specified.","tags":["QC Checklist"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Newly created QC checklist item","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","createdAt","updatedAt"]}},"required":["item"]}}}},"400":{"description":"Invalid request body"},"409":{"description":"QC checklist item limit reached"},"500":{"description":"Internal server error"}}}},"/qc-checklist/reorder":{"post":{"operationId":"reorderQcChecklistItems","description":"Reorder QC checklist items by providing an ordered array of item IDs. Each item's sort order is updated to match its position.","tags":["QC Checklist"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":200}},"required":["ids"]}}}},"responses":{"200":{"description":"QC checklist items successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"One or more QC checklist items not found"},"500":{"description":"Internal server error"}}}},"/qc-checklist/{id}":{"put":{"operationId":"updateQcChecklistItem","description":"Update an existing QC checklist item's name, active status, or sort order.","tags":["QC Checklist"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"QC checklist item ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"QC checklist item ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Successfully updated QC checklist item","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","createdAt","updatedAt"]}},"required":["item"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"QC checklist item not found"},"409":{"description":"Active QC checklist item limit reached"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteQcChecklistItem","description":"Soft-delete a QC checklist item by marking it as inactive. The item remains in the database for historical reference.","tags":["QC Checklist"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"QC checklist item ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"QC checklist item ID","name":"id","in":"path"}],"responses":{"200":{"description":"QC checklist item deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","sortOrder","isActive","createdAt","updatedAt"]}},"required":["item"]}}}},"404":{"description":"QC checklist item not found"},"500":{"description":"Internal server error"}}}},"/qc-settings":{"get":{"operationId":"getQcSettings","description":"Retrieve quality control settings for the current lab. Returns an unpersisted default projection with QC disabled when no row exists; writes occur only through the manager-guarded update command.","tags":["QC Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"QC settings for the current lab","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"qcEnabled":{"type":"boolean"},"selectAllEnabled":{"type":"boolean"},"automationEnabled":{"type":"boolean"},"automationId":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","qcEnabled","selectAllEnabled","automationEnabled","automationId","createdAt","updatedAt"]}},"required":["settings"]}}}},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateQcSettings","description":"Update quality control settings for the current lab. Creates default settings if none exist (upsert). Controls QC enabled state, select-all, and automation.","tags":["QC Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"qcEnabled":{"type":"boolean"},"selectAllEnabled":{"type":"boolean"},"automationEnabled":{"type":"boolean"},"automationId":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"Successfully updated QC settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"qcEnabled":{"type":"boolean"},"selectAllEnabled":{"type":"boolean"},"automationEnabled":{"type":"boolean"},"automationId":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","qcEnabled","selectAllEnabled","automationEnabled","automationId","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/receiving-categories":{"get":{"operationId":"listReceivingCategories","description":"List all persisted receiving categories for the current lab with optional sources. Defaults are initialized during organization creation or by the explicit settings-defaults command.","tags":["Receiving"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":true,"example":true},"required":false,"name":"includeSources","in":"query"}],"responses":{"200":{"description":"List of receiving categories with optional nested sources","content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"description":{"type":["string","null"]},"showInFlow":{"type":"boolean"},"settings":{"type":["object","null"],"properties":{"unitsPerItemLimit":{"type":"integer","minimum":1,"maximum":100}},"additionalProperties":false},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","code","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["id","labId","name","code","description","showInFlow","settings","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["categories"]}}}},"400":{"description":"Invalid query parameters"},"500":{"description":"Internal server error"}}}},"/receiving-categories/{id}":{"put":{"operationId":"updateReceivingCategory","description":"Update a receiving category's visibility in the receiving flow, settings (e.g. units per item limit), or active status.","tags":["Receiving"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Receiving category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Receiving category ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"showInFlow":{"type":"boolean"},"settings":{"type":"object","properties":{"unitsPerItemLimit":{"type":"integer","minimum":1,"maximum":100}},"additionalProperties":false},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Successfully updated receiving category","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"description":{"type":["string","null"]},"showInFlow":{"type":"boolean"},"settings":{"type":["object","null"],"properties":{"unitsPerItemLimit":{"type":"integer","minimum":1,"maximum":100}},"additionalProperties":false},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","code","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["id","labId","name","code","description","showInFlow","settings","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Receiving category not found"},"500":{"description":"Internal server error"}}}},"/receiving-sources":{"post":{"operationId":"createReceivingSource","description":"Create a new receiving source within a category (e.g., a specific scanner brand under the Digital category). Validates the category belongs to the lab.","tags":["Receiving"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":100},"code":{"type":"string","minLength":1,"maxLength":50},"sortOrder":{"type":"integer","minimum":0}},"required":["categoryId","name"]}}}},"responses":{"201":{"description":"Newly created receiving source","content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","code","sortOrder","isActive","createdAt","updatedAt"]}},"required":["source"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Parent category not found"},"500":{"description":"Internal server error"}}}},"/receiving-sources/reorder":{"post":{"operationId":"reorderReceivingSources","description":"Reorder receiving sources within a category by providing an ordered array of source IDs. Validates all sources belong to the specified category.","tags":["Receiving"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":"string","minLength":1},"sourceIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["categoryId","sourceIds"]}}}},"responses":{"200":{"description":"Receiving sources successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Category or one or more sources not found"},"500":{"description":"Internal server error"}}}},"/receiving-sources/{id}":{"put":{"operationId":"updateReceivingSource","description":"Update a receiving source's name, code, or active status.","tags":["Receiving"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Receiving source ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Receiving source ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"code":{"type":["string","null"],"minLength":1,"maxLength":50},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Successfully updated receiving source","content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","code","sortOrder","isActive","createdAt","updatedAt"]}},"required":["source"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Receiving source not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteReceivingSource","description":"Soft-delete a receiving source by marking it as inactive. The source remains in the database for historical reference.","tags":["Receiving"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Receiving source ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Receiving source ID","name":"id","in":"path"}],"responses":{"200":{"description":"Receiving source deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","code","sortOrder","isActive","createdAt","updatedAt"]}},"required":["source"]}}}},"404":{"description":"Receiving source not found"},"500":{"description":"Internal server error"}}}},"/remake-reasons":{"get":{"operationId":"listRemakeReasons","description":"List all remake reasons for the current lab, sorted by display order. Each reason includes its associated discount percentage.","tags":["Remake Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of remake reasons for the lab","content":{"application/json":{"schema":{"type":"object","properties":{"reasons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"discountPercent":{"type":"number"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","discountPercent","sortOrder","isActive","createdAt","updatedAt"]}},"truncated":{"type":"boolean","description":"True when the lab holds more reasons than one response returns. The list is capped, not refused."}},"required":["reasons","truncated"]}}}},"400":{"description":"Invalid query parameters"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createRemakeReason","description":"Create a new remake reason with an optional discount percentage. Automatically assigns the next sort order if not specified.","tags":["Remake Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"discountPercent":{"type":["integer","null"],"minimum":0,"maximum":100,"default":0},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Newly created remake reason","content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"discountPercent":{"type":"number"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","discountPercent","sortOrder","isActive","createdAt","updatedAt"]}},"required":["reason"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Lab not found"},"409":{"description":"Remake reason row limit reached"},"500":{"description":"Internal server error"}}}},"/remake-reasons/reorder":{"post":{"operationId":"reorderRemakeReasons","description":"Reorder remake reasons by providing an ordered array of reason IDs. Each reason's sort order is updated to match its position.","tags":["Remake Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1}}},"required":["ids"]}}}},"responses":{"200":{"description":"Remake reasons successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"One or more remake reasons not found"},"500":{"description":"Internal server error"}}}},"/remake-reasons/{id}":{"put":{"operationId":"updateRemakeReason","description":"Update an existing remake reason's name, discount percentage, active status, or sort order.","tags":["Remake Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Remake reason ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Remake reason ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"discountPercent":{"type":["integer","null"],"minimum":0,"maximum":100,"default":0},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Successfully updated remake reason","content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"discountPercent":{"type":"number"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","discountPercent","sortOrder","isActive","createdAt","updatedAt"]}},"required":["reason"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Remake reason not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteRemakeReason","description":"Soft-delete a remake reason by marking it as inactive. The reason remains in the database for historical reference.","tags":["Remake Reasons"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Remake reason ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Remake reason ID","name":"id","in":"path"}],"responses":{"200":{"description":"Remake reason deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"discountPercent":{"type":"number"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","discountPercent","sortOrder","isActive","createdAt","updatedAt"]}},"required":["reason"]}}}},"404":{"description":"Remake reason not found"},"500":{"description":"Internal server error"}}}},"/sticker-templates/categories":{"get":{"operationId":"listStickerTemplateCategories","description":"List sticker template categories for the lab with pagination, search, and template count per category. Clinic users must provide a labId.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","default":"false","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Paginated list of sticker template categories with template counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"templateCount":{"type":"number"}},"required":["id","labId","name","description","sortOrder","isActive","createdAt","updatedAt","templateCount"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters or missing labId for clinic users"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createStickerTemplateCategory","description":"Create a new sticker template category for organizing sticker templates. Lab-only operation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Newly created sticker template category","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"templateCount":{"type":"number"}},"required":["id","labId","name","description","sortOrder","isActive","createdAt","updatedAt","templateCount"]}},"required":["category"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/sticker-templates/categories/{id}":{"get":{"operationId":"getStickerTemplateCategory","description":"Retrieve a single sticker template category by ID.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Sticker template category details","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"404":{"description":"Category not found"},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateStickerTemplateCategory","description":"Update a sticker template category's name, description, sort order, or active status. Lab-only operation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Successfully updated sticker template category","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Category not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteStickerTemplateCategory","description":"Soft-delete a sticker template category by marking it as inactive. Lab-only operation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Category deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Category not found"},"500":{"description":"Internal server error"}}}},"/sticker-templates/generate":{"post":{"operationId":"generateStickerPdf","description":"Generate sticker PDF data for a specific order using a template. Resolves order variables (patient, clinic, doctor, product category) and returns processed template data with inputs for PDF generation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"templateId":{"type":"string","minLength":1},"orderId":{"type":"string","minLength":1},"copies":{"type":"integer","exclusiveMinimum":0,"maximum":50,"default":1}},"required":["templateId","orderId"]}}}},"responses":{"200":{"description":"Processed sticker template with resolved variables and PDF inputs","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"width":{"type":"string"},"height":{"type":"string"},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"previewImageKey":{"type":["string","null"]},"isDefault":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","description","width","height","templateData","previewImageKey","isDefault","sortOrder","isActive","createdAt","updatedAt"]},"variables":{"type":"object","additionalProperties":{"type":"string"}},"inputs":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}}},"copies":{"type":"number"}},"required":["template","variables","inputs","copies"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Template or order not found"},"500":{"description":"Internal server error"}}}},"/sticker-templates":{"get":{"operationId":"listStickerTemplates","description":"List sticker templates for the lab with pagination, search, and optional category filtering. Includes joined category data. Clinic users must provide a labId.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"categoryId","in":"query"},{"schema":{"type":"string","default":"false","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Paginated list of sticker templates with category details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"width":{"type":"string"},"height":{"type":"string"},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"previewImageKey":{"type":["string","null"]},"isDefault":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"category":{"type":["object","null"],"properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["id","labId","categoryId","name","description","width","height","templateData","previewImageKey","isDefault","sortOrder","isActive","createdAt","updatedAt","category"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters or missing labId for clinic users"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createStickerTemplate","description":"Create a new sticker template with dimensions, template data, and optional category. If marked as default, unsets the previous default template. Lab-only operation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":["string","null"]},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"width":{"type":"number","exclusiveMinimum":0,"default":50},"height":{"type":"number","exclusiveMinimum":0,"default":25},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"isDefault":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name","templateData"]}}}},"responses":{"201":{"description":"Newly created sticker template","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"width":{"type":"string"},"height":{"type":"string"},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"previewImageKey":{"type":["string","null"]},"isDefault":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","description","width","height","templateData","previewImageKey","isDefault","sortOrder","isActive","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Invalid request body or category not found"},"500":{"description":"Internal server error"}}}},"/sticker-templates/{id}":{"get":{"operationId":"getStickerTemplate","description":"Retrieve a single sticker template by ID, including its full template data and joined category information.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Sticker template details with category","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"width":{"type":"string"},"height":{"type":"string"},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"previewImageKey":{"type":["string","null"]},"isDefault":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"category":{"type":["object","null"],"properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["id","labId","categoryId","name","description","width","height","templateData","previewImageKey","isDefault","sortOrder","isActive","createdAt","updatedAt","category"]}},"required":["template"]}}}},"404":{"description":"Template not found"},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateStickerTemplate","description":"Update an existing sticker template's properties including name, dimensions, template data, category, and default status. Lab-only operation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":["string","null"]},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"width":{"type":"number","exclusiveMinimum":0,"default":50},"height":{"type":"number","exclusiveMinimum":0,"default":25},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"isDefault":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Successfully updated sticker template","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"width":{"type":"string"},"height":{"type":"string"},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"previewImageKey":{"type":["string","null"]},"isDefault":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","description","width","height","templateData","previewImageKey","isDefault","sortOrder","isActive","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Invalid request body or category not found"},"404":{"description":"Template not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteStickerTemplate","description":"Soft-delete a sticker template by marking it as inactive. Lab-only operation.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Sticker template deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Template not found"},"500":{"description":"Internal server error"}}}},"/sticker-templates/{id}/preview":{"get":{"operationId":"previewStickerTemplate","description":"Generate a preview of a sticker template with sample variables substituted. Returns processed template data ready for rendering.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Sticker template preview with sample variables applied","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"width":{"type":"string"},"height":{"type":"string"},"templateData":{"type":"object","properties":{"basePdf":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"padding":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]}},"required":["width","height"],"additionalProperties":{}}]},"schemas":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minItems":1},"sampledata":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["basePdf","schemas"],"additionalProperties":{}},"previewImageKey":{"type":["string","null"]},"isDefault":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","name","description","width","height","templateData","previewImageKey","isDefault","sortOrder","isActive","createdAt","updatedAt"]},"sampleVariables":{"type":"object","additionalProperties":{"type":"string"}}},"required":["template","sampleVariables"]}}}},"404":{"description":"Template not found"},"500":{"description":"Internal server error"}}}},"/sticker-templates/{id}/preview-image":{"get":{"operationId":"getStickerTemplatePreviewImage","description":"Serve the managed preview image for a sticker template.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Sticker preview image binary"},"404":{"description":"Template or preview image not found"}}},"post":{"operationId":"uploadStickerTemplatePreviewImage","description":"Upload or replace a sticker template preview image.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Preview image attached","content":{"application/json":{"schema":{"type":"object","properties":{"previewImageKey":{"type":"string"}},"required":["previewImageKey"]}}}},"400":{"description":"Invalid image"},"404":{"description":"Template not found"}}},"delete":{"operationId":"deleteStickerTemplatePreviewImage","description":"Detach a sticker preview image and queue its object deletion.","tags":["Sticker Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Preview image removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Template or preview image not found"}}}},"/pickup-requests/settings":{"get":{"tags":["Pickup Requests"],"operationId":"getPickupRequestSettings","summary":"Get pickup request settings","description":"Retrieve pickup request form settings for the current lab, including whether the form is enabled and thank-you message configuration. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Pickup request settings with form-enabled status and thank-you messages","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"formEnabled":{"type":"boolean"},"thankYouTitle":{"type":"string"},"thankYouMessage":{"type":"string"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","formEnabled","thankYouTitle","thankYouMessage","createdAt","updatedAt"]}},"required":["settings"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"500":{"description":"Internal server error"}}},"put":{"tags":["Pickup Requests"],"operationId":"updatePickupRequestSettings","summary":"Update pickup request settings","description":"Update pickup request form settings. Creates default settings if none exist. Supports partial updates. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"formEnabled":{"type":"boolean"},"thankYouTitle":{"type":"string","maxLength":200},"thankYouMessage":{"type":"string","maxLength":1000}}}}}},"responses":{"200":{"description":"Updated pickup request settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"500":{"description":"Internal server error"}}}},"/pickup-requests/elements":{"get":{"tags":["Pickup Requests"],"operationId":"listPickupFormElements","summary":"List pickup form elements","description":"Retrieve paginated form elements for the pickup request form. Elements are ordered by sortOrder. Optionally includes inactive elements. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Ordered list of form elements","content":{"application/json":{"schema":{"type":"object","properties":{"elements":{"type":"array","items":{}}},"required":["elements"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"500":{"description":"Internal server error"}}},"post":{"tags":["Pickup Requests"],"operationId":"createPickupFormElement","summary":"Create a pickup form element","description":"Add a new form element to the pickup request form. If no sortOrder is specified, it will be appended at the end. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"elementType":{"type":"string","enum":["header","subheader","text","textarea","address","email","phone","date","checkbox"]},"label":{"type":"string","minLength":1,"maxLength":200},"placeholder":{"type":"string","maxLength":200},"helpText":{"type":"string","maxLength":500},"isRequired":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["elementType","label"]}}}},"responses":{"201":{"description":"Form element created","content":{"application/json":{"schema":{"type":"object","properties":{"element":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"500":{"description":"Internal server error"}}}},"/pickup-requests/elements/reorder":{"put":{"tags":["Pickup Requests"],"operationId":"reorderPickupFormElements","summary":"Reorder pickup form elements","description":"Update the sort order of multiple form elements in a single request. All referenced elements must belong to the current lab. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"sortOrder":{"type":"integer","minimum":0}},"required":["id","sortOrder"]}}},"required":["items"]}}}},"responses":{"200":{"description":"Form elements reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Some elements not found or do not belong to this lab"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"500":{"description":"Internal server error"}}}},"/pickup-requests/elements/{id}":{"get":{"tags":["Pickup Requests"],"operationId":"getPickupFormElement","summary":"Get a pickup form element by ID","description":"Retrieve a single form element by its ID. Must belong to the current lab. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Form element details","content":{"application/json":{"schema":{"type":"object","properties":{"element":{}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"404":{"description":"Element not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Pickup Requests"],"operationId":"updatePickupFormElement","summary":"Update a pickup form element","description":"Update one or more fields of a form element. Supports partial updates. Must belong to the current lab. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"elementType":{"type":"string","enum":["header","subheader","text","textarea","address","email","phone","date","checkbox"]},"label":{"type":"string","minLength":1,"maxLength":200},"placeholder":{"type":"string","maxLength":200},"helpText":{"type":"string","maxLength":500},"isRequired":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Updated form element","content":{"application/json":{"schema":{"type":"object","properties":{"element":{}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"404":{"description":"Element not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Pickup Requests"],"operationId":"deletePickupFormElement","summary":"Deactivate a pickup form element","description":"Soft-delete a form element by setting it to inactive. The element remains in the database but will not appear in active element lists. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Form element deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"element":{}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"404":{"description":"Element not found"},"500":{"description":"Internal server error"}}}},"/pickup-requests/form/{partnershipId}":{"get":{"tags":["Pickup Requests"],"operationId":"getClinicPickupRequestForm","summary":"Get the current pickup form for an active partnership","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"partnershipId","in":"path"}],"responses":{"200":{"description":"Current immutable form revision","content":{"application/json":{"schema":{"type":"object","properties":{"formRevisionId":{"type":"string"},"revision":{"type":"integer","exclusiveMinimum":0},"definition":{"type":"array","items":{"type":"object","additionalProperties":{}}},"thankYouTitle":{"type":"string"},"thankYouMessage":{"type":"string"}},"required":["formRevisionId","revision","definition","thankYouTitle","thankYouMessage"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Clinic partnership access required"},"404":{"description":"Pickup form not found"},"409":{"description":"Pickup requests are disabled or not yet published"}}}},"/pickup-requests":{"post":{"tags":["Pickup Requests"],"operationId":"createPickupRequest","summary":"Create a pickup request as a partner clinic","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"partnershipId":{"type":"string","minLength":1},"formRevisionId":{"type":"string","minLength":1},"commandId":{"type":"string","minLength":1,"maxLength":200},"formData":{"type":"object","additionalProperties":{},"default":{}},"requestedWindow":{"type":"object","properties":{"start":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"end":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"timezone":{"type":"string","minLength":1,"maxLength":100}},"required":["start","end","timezone"],"additionalProperties":false},"address":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["saved"]},"locationId":{"type":"string","minLength":1}},"required":["type","locationId"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["one_off"]},"address":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false}},"required":["type","address"],"additionalProperties":false}]},"orderIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":100,"default":[]},"notes":{"type":["string","null"],"maxLength":1000}},"required":["partnershipId","formRevisionId","commandId","requestedWindow","address"],"additionalProperties":false}}}},"responses":{"200":{"description":"Idempotently replayed pickup request","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]}},"required":["request"]}}}},"201":{"description":"Pickup request created","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]}},"required":["request"]}}}},"400":{"description":"Invalid form, address, window, or orders"},"401":{"description":"Unauthorized"},"403":{"description":"Clinic partnership access required"},"404":{"description":"Partnership, form revision, or location not found"},"409":{"description":"Pickup form disabled or idempotency conflict"}}},"get":{"tags":["Pickup Requests"],"operationId":"listPickupRequests","summary":"List pickup requests visible to the current organization","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["requested","scheduled","en_route","needs_reschedule","collected","cancelled"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Pickup requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]}},"page":{"type":"integer"},"limit":{"type":"integer"}},"required":["requests","page","limit"]}}}},"401":{"description":"Unauthorized"}}}},"/pickup-requests/{id}":{"get":{"tags":["Pickup Requests"],"operationId":"getPickupRequest","summary":"Get a pickup request and its immutable history","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Pickup request detail","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]},"orderIds":{"type":"array","items":{"type":"string"}},"attempts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"attemptNumber":{"type":"integer","exclusiveMinimum":0},"outcome":{"type":"string"},"notes":{"type":["string","null"]},"attemptedAt":{"type":"string"}},"required":["id","attemptNumber","outcome","notes","attemptedAt"]}},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sequence":{"type":"integer","exclusiveMinimum":0},"eventType":{"type":"string"},"fromStatus":{"type":["string","null"]},"toStatus":{"type":["string","null"]},"payload":{"type":"object","additionalProperties":{}},"createdAt":{"type":"string"}},"required":["id","sequence","eventType","fromStatus","toStatus","payload","createdAt"]}}},"required":["request","orderIds","attempts","events"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Pickup request not found"}}},"put":{"tags":["Pickup Requests"],"operationId":"updateRequestedPickup","summary":"Edit a clinic pickup request before it is scheduled","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"formData":{"type":"object","additionalProperties":{}},"requestedWindow":{"type":"object","properties":{"start":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"end":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"timezone":{"type":"string","minLength":1,"maxLength":100}},"required":["start","end","timezone"],"additionalProperties":false},"address":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["saved"]},"locationId":{"type":"string","minLength":1}},"required":["type","locationId"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["one_off"]},"address":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false}},"required":["type","address"],"additionalProperties":false}]},"orderIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":100},"notes":{"type":["string","null"],"maxLength":1000}},"required":["commandId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Updated or idempotently replayed pickup request","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]}},"required":["request"]}}}},"400":{"description":"Invalid form, address, window, or orders"},"401":{"description":"Unauthorized"},"403":{"description":"Clinic access required"},"404":{"description":"Pickup request not found"},"409":{"description":"Request is scheduled or command conflicts"}}}},"/pickup-requests/{id}/commands":{"post":{"tags":["Pickup Requests"],"operationId":"commandPickupRequest","summary":"Schedule, assign, route, collect, reschedule, or cancel a pickup","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","enum":["schedule"]},"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedStatus":{"type":"string","enum":["requested","needs_reschedule"]},"window":{"type":"object","properties":{"start":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"end":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"timezone":{"type":"string","minLength":1,"maxLength":100}},"required":["start","end","timezone"],"additionalProperties":false},"assignment":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["unassigned"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["member"]},"memberId":{"type":"string","minLength":1}},"required":["type","memberId"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["external"]},"name":{"type":"string","minLength":1,"maxLength":200},"phone":{"type":"string","minLength":1,"maxLength":80},"email":{"type":"string","maxLength":320,"format":"email"}},"required":["type","name"],"additionalProperties":false}],"default":{"type":"unassigned"}}},"required":["action","commandId","expectedStatus","window"],"additionalProperties":false},{"type":"object","properties":{"action":{"type":"string","enum":["start_route"]},"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedStatus":{"type":"string","enum":["scheduled"]}},"required":["action","commandId","expectedStatus"],"additionalProperties":false},{"type":"object","properties":{"action":{"type":"string","enum":["needs_reschedule"]},"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedStatus":{"type":"string","enum":["scheduled","en_route"]},"notes":{"type":"string","minLength":1,"maxLength":2000}},"required":["action","commandId","expectedStatus","notes"],"additionalProperties":false},{"type":"object","properties":{"action":{"type":"string","enum":["collect"]},"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedStatus":{"type":"string","enum":["en_route"]},"notes":{"type":["string","null"],"maxLength":2000}},"required":["action","commandId","expectedStatus"],"additionalProperties":false},{"type":"object","properties":{"action":{"type":"string","enum":["cancel"]},"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedStatus":{"type":"string","enum":["requested","scheduled","en_route","needs_reschedule"]},"notes":{"type":"string","minLength":1,"maxLength":2000}},"required":["action","commandId","expectedStatus","notes"],"additionalProperties":false},{"type":"object","properties":{"action":{"type":"string","enum":["assign"]},"commandId":{"type":"string","minLength":1,"maxLength":200},"expectedStatus":{"type":"string","enum":["scheduled","en_route"]},"assignment":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["unassigned"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["member"]},"memberId":{"type":"string","minLength":1}},"required":["type","memberId"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["external"]},"name":{"type":"string","minLength":1,"maxLength":200},"phone":{"type":"string","minLength":1,"maxLength":80},"email":{"type":"string","maxLength":320,"format":"email"}},"required":["type","name"],"additionalProperties":false}]}},"required":["action","commandId","expectedStatus","assignment"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Command applied or idempotently replayed","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]}},"required":["request"]}}}},"400":{"description":"Invalid command"},"401":{"description":"Unauthorized"},"403":{"description":"Lab access required"},"404":{"description":"Pickup request or assignee not found"},"409":{"description":"Stale status or idempotency conflict"}}}},"/pickup-requests/{id}/attempts":{"post":{"tags":["Pickup Requests"],"operationId":"createPickupAttempt","summary":"Record a pickup attempt","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"outcome":{"type":"string","enum":["collected","no_answer","not_ready","address_issue","other"]},"notes":{"type":["string","null"],"maxLength":2000},"attemptedAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"}},"required":["commandId","outcome"],"additionalProperties":false}}}},"responses":{"200":{"description":"Attempt recorded or idempotently replayed","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"clinicId":{"type":"string"},"partnershipId":{"type":"string"},"formRevisionId":{"type":"string"},"status":{"type":"string"},"formData":{"type":"object","additionalProperties":{}},"requestedWindowStart":{"type":"string"},"requestedWindowEnd":{"type":"string"},"requestedTimezone":{"type":"string"},"scheduledWindowStart":{"type":["string","null"]},"scheduledWindowEnd":{"type":["string","null"]},"scheduledTimezone":{"type":["string","null"]},"addressId":{"type":["string","null"]},"addressSnapshot":{"type":"object","properties":{"label":{"type":["string","null"],"minLength":1,"maxLength":120},"contactName":{"type":["string","null"],"minLength":1,"maxLength":200},"contactPhone":{"type":["string","null"],"minLength":1,"maxLength":80},"addressLine1":{"type":"string","minLength":1,"maxLength":300},"addressLine2":{"type":["string","null"],"maxLength":300},"city":{"type":"string","minLength":1,"maxLength":160},"state":{"type":["string","null"],"maxLength":160},"postcode":{"type":"string","minLength":1,"maxLength":40},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["addressLine1","city","postcode","country"],"additionalProperties":false},"requestedByMemberId":{"type":"string"},"assignedMemberId":{"type":["string","null"]},"assignedExternalName":{"type":["string","null"]},"assignedExternalPhone":{"type":["string","null"]},"assignedExternalEmail":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"collectedAt":{"type":["string","null"]},"cancelledAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","clinicId","partnershipId","formRevisionId","status","formData","requestedWindowStart","requestedWindowEnd","requestedTimezone","scheduledWindowStart","scheduledWindowEnd","scheduledTimezone","addressId","addressSnapshot","requestedByMemberId","assignedMemberId","assignedExternalName","assignedExternalPhone","assignedExternalEmail","notes","internalNotes","collectedAt","cancelledAt","createdAt","updatedAt"]},"attempt":{"type":"object","properties":{"id":{"type":"string"},"attemptNumber":{"type":"integer","exclusiveMinimum":0},"outcome":{"type":"string"},"notes":{"type":["string","null"]},"attemptedAt":{"type":"string"}},"required":["id","attemptNumber","outcome","notes","attemptedAt"]}},"required":["request","attempt"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Lab access required"},"404":{"description":"Pickup request not found"},"409":{"description":"Pickup is not en route or command conflicts"}}}},"/email-templates":{"get":{"operationId":"listEmailTemplates","description":"List email templates for the current lab with filtering by category, search, and pagination. Supports including inactive templates.","tags":["Email Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["general","order","invoice","shipment","notification","marketing"]},"required":false,"name":"category","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}]},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of email templates","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"description":{"type":["string","null"]},"designJson":{"type":"object","properties":{"body":{"type":"object","properties":{"rows":{"type":"array","items":{}}},"required":["rows"],"additionalProperties":{}},"counters":{"type":"object","additionalProperties":{"type":"number"}}},"required":["body"],"additionalProperties":{}},"htmlContent":{"type":"string"},"category":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","subject","description","designJson","htmlContent","category","sortOrder","isActive","createdAt","updatedAt"]}},"page":{"type":"number"},"limit":{"type":"number"}},"required":["templates","page","limit"]}}}},"400":{"description":"Invalid query parameters"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createEmailTemplate","description":"Create a new email template with design JSON and HTML content. Supports categorization (general, order, invoice, shipment, notification, marketing).","tags":["Email Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"subject":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":500},"designJson":{"type":"object","properties":{"body":{"type":"object","properties":{"rows":{"type":"array","items":{}}},"required":["rows"],"additionalProperties":{}},"counters":{"type":"object","additionalProperties":{"type":"number"}}},"required":["body"],"additionalProperties":{}},"htmlContent":{"type":"string","minLength":1},"category":{"type":"string","enum":["general","order","invoice","shipment","notification","marketing"],"default":"general"},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name","subject","designJson","htmlContent"]}}}},"responses":{"201":{"description":"Newly created email template","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"description":{"type":["string","null"]},"designJson":{"type":"object","properties":{"body":{"type":"object","properties":{"rows":{"type":"array","items":{}}},"required":["rows"],"additionalProperties":{}},"counters":{"type":"object","additionalProperties":{"type":"number"}}},"required":["body"],"additionalProperties":{}},"htmlContent":{"type":"string"},"category":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","subject","description","designJson","htmlContent","category","sortOrder","isActive","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/email-templates/{id}":{"get":{"operationId":"getEmailTemplate","description":"Retrieve a single email template by ID, including its full design JSON and HTML content.","tags":["Email Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Email template details","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"description":{"type":["string","null"]},"designJson":{"type":"object","properties":{"body":{"type":"object","properties":{"rows":{"type":"array","items":{}}},"required":["rows"],"additionalProperties":{}},"counters":{"type":"object","additionalProperties":{"type":"number"}}},"required":["body"],"additionalProperties":{}},"htmlContent":{"type":"string"},"category":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","subject","description","designJson","htmlContent","category","sortOrder","isActive","createdAt","updatedAt"]}},"required":["template"]}}}},"404":{"description":"Email template not found"},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateEmailTemplate","description":"Update an existing email template's name, subject, content, category, or other properties.","tags":["Email Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"subject":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":500},"designJson":{"type":"object","properties":{"body":{"type":"object","properties":{"rows":{"type":"array","items":{}}},"required":["rows"],"additionalProperties":{}},"counters":{"type":"object","additionalProperties":{"type":"number"}}},"required":["body"],"additionalProperties":{}},"htmlContent":{"type":"string","minLength":1},"category":{"type":"string","enum":["general","order","invoice","shipment","notification","marketing"],"default":"general"},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Successfully updated email template","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"description":{"type":["string","null"]},"designJson":{"type":"object","properties":{"body":{"type":"object","properties":{"rows":{"type":"array","items":{}}},"required":["rows"],"additionalProperties":{}},"counters":{"type":"object","additionalProperties":{"type":"number"}}},"required":["body"],"additionalProperties":{}},"htmlContent":{"type":"string"},"category":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","subject","description","designJson","htmlContent","category","sortOrder","isActive","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Email template not found"},"409":{"description":"Template is used by an active production task"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteEmailTemplate","description":"Soft-delete an email template by marking it as inactive. The template remains in the database for historical reference.","tags":["Email Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Email template deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Email template not found"},"409":{"description":"Template is used by an active production task"},"500":{"description":"Internal server error"}}}},"/scanner-settings":{"get":{"operationId":"getScannerSettings","description":"Retrieve scanner settings for the current organization, including auto-pool case amount, disconnection email preferences, and folder paths.","tags":["Scanner Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Scanner settings for the current organization","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"organizationId":{"type":"string"},"autoPoolCaseAmount":{"type":"number"},"sendDisconnectionEmail":{"type":"boolean"},"disconnectionEmailRecipients":{"type":"array","items":{"type":"string"}},"newOrderFolderPath":{"type":["string","null"]},"existingOrderFolderPath":{"type":["string","null"]},"useScanhubLite":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","organizationId","autoPoolCaseAmount","sendDisconnectionEmail","disconnectionEmailRecipients","newOrderFolderPath","existingOrderFolderPath","useScanhubLite","createdAt","updatedAt"]}},"required":["settings"]}}}},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateScannerSettings","description":"Update scanner settings for the current organization. Creates default settings if none exist (upsert). Controls auto-pool amount, disconnection emails, folder paths, and ScanHub Lite mode.","tags":["Scanner Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"autoPoolCaseAmount":{"type":["integer","null"],"minimum":0},"sendDisconnectionEmail":{"type":"boolean"},"disconnectionEmailRecipients":{"type":"array","items":{"type":"string","format":"email"}},"newOrderFolderPath":{"type":["string","null"]},"existingOrderFolderPath":{"type":["string","null"]},"useScanhubLite":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Successfully updated scanner settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"organizationId":{"type":"string"},"autoPoolCaseAmount":{"type":"number"},"sendDisconnectionEmail":{"type":"boolean"},"disconnectionEmailRecipients":{"type":"array","items":{"type":"string"}},"newOrderFolderPath":{"type":["string","null"]},"existingOrderFolderPath":{"type":["string","null"]},"useScanhubLite":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","organizationId","autoPoolCaseAmount","sendDisconnectionEmail","disconnectionEmailRecipients","newOrderFolderPath","existingOrderFolderPath","useScanhubLite","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/scanner-connections":{"get":{"operationId":"listScannerConnections","description":"List all scanner connections for the current organization. Optionally filter by scanner type and include inactive connections. Includes Medit group metadata when available.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}]},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","enum":["3shape","itero","medit"]},"required":false,"name":"scannerType","in":"query"}],"responses":{"200":{"description":"List of scanner connections with metadata","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"scannerType":{"type":"string"},"name":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"testMode":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastConnectedAt":{"type":["string","null"]},"lastSyncAt":{"type":["string","null"]},"meditGroupName":{"type":["string","null"]},"meditGroupType":{"type":["string","null"]}},"required":["id","organizationId","scannerType","name","connectionStatus","statusMessage","testMode","sortOrder","isActive","createdAt","updatedAt","lastConnectedAt","lastSyncAt","meditGroupName","meditGroupType"]}}},"required":["data"]}}}},"400":{"description":"Invalid query parameters"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createScannerConnection","description":"Create a new scanner connection for the organization. Supports the implemented Medit, 3Shape, and iTero providers.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scannerType":{"type":"string","enum":["3shape","itero","medit"]},"name":{"type":"string","minLength":1,"maxLength":100},"testMode":{"type":"boolean","default":false},"sortOrder":{"type":"integer","default":0}},"required":["scannerType","name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Newly created scanner connection","content":{"application/json":{"schema":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"scannerType":{"type":"string"},"name":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"testMode":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastConnectedAt":{"type":["string","null"]},"lastSyncAt":{"type":["string","null"]}},"required":["id","organizationId","scannerType","name","connectionStatus","statusMessage","testMode","sortOrder","isActive","createdAt","updatedAt","lastConnectedAt","lastSyncAt"]}},"required":["connection"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/scanner-connections/medit/callback":{"get":{"operationId":"handleMeditOAuthCallback","description":"Handle the Medit OAuth callback after user authorization. Exchanges the authorization code for tokens, stores encrypted credentials, and redirects to scanner settings.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"302":{"description":"Redirect to scanner settings page after OAuth flow"},"500":{"description":"Internal server error"}}}},"/scanner-connections/itero/callback":{"get":{"operationId":"handleIteroOAuthCallback","description":"Handle the iTero OAuth callback after user authorization. Exchanges the authorization code for tokens, discovers the API region, and pairs the account if a single unpaired account is found.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"302":{"description":"Redirect to scanner settings page after OAuth flow"},"500":{"description":"Internal server error"}}}},"/scanner-connections/3shape/callback":{"get":{"operationId":"handle3ShapeOAuthCallback","description":"Handle the 3Shape OAuth callback after user authorization. Exchanges the code using PKCE, stores encrypted tokens, and discovers active regions.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"302":{"description":"Redirect to scanner settings page after OAuth flow"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}":{"get":{"operationId":"getScannerConnection","description":"Retrieve a single scanner connection by ID, including scanner-type-specific metadata (e.g. Medit group info).","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Scanner connection details with metadata","content":{"application/json":{"schema":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"scannerType":{"type":"string"},"name":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"testMode":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastConnectedAt":{"type":["string","null"]},"lastSyncAt":{"type":["string","null"]},"meditGroupName":{"type":["string","null"]},"meditGroupType":{"type":["string","null"]}},"required":["id","organizationId","scannerType","name","connectionStatus","statusMessage","testMode","sortOrder","isActive","createdAt","updatedAt","lastConnectedAt","lastSyncAt","meditGroupName","meditGroupType"]}},"required":["connection"]}}}},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateScannerConnection","description":"Update a scanner connection's name, test mode, sort order, or active status.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"testMode":{"type":"boolean"},"sortOrder":{"type":"integer"},"isActive":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Successfully updated scanner connection","content":{"application/json":{"schema":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"scannerType":{"type":"string"},"name":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"testMode":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastConnectedAt":{"type":["string","null"]},"lastSyncAt":{"type":["string","null"]}},"required":["id","organizationId","scannerType","name","connectionStatus","statusMessage","testMode","sortOrder","isActive","createdAt","updatedAt","lastConnectedAt","lastSyncAt"]}},"required":["connection"]}}}},"400":{"description":"Invalid request body"},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteScannerConnection","description":"Soft-delete a scanner connection by marking it as inactive.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Scanner connection deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}/disconnect":{"post":{"operationId":"disconnectScannerConnection","description":"Disconnect a scanner connection by removing stored tokens and scanner-specific data. For iTero, attempts to unpair the account first. Resets connection status to disconnected.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Scanner connection successfully disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"connection":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"scannerType":{"type":"string"},"name":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"testMode":{"type":"boolean"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastConnectedAt":{"type":["string","null"]},"lastSyncAt":{"type":["string","null"]}},"required":["id","organizationId","scannerType","name","connectionStatus","statusMessage","testMode","sortOrder","isActive","createdAt","updatedAt","lastConnectedAt","lastSyncAt"]}},"required":["success"]}}}},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}/test":{"post":{"operationId":"testScannerConnection","description":"Test a scanner connection by attempting an API call to the scanner provider. Supports Medit, 3Shape, and iTero. Updates connection status based on the test result.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Test result with success status and scanner details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["success"]}}}},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}/sync":{"post":{"operationId":"syncScannerConnection","description":"Trigger a data sync for a scanner connection. Fetches the latest cases/orders from the scanner provider (Medit, 3Shape, or iTero) and updates the last sync timestamp.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Sync result with fetched data","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{},"syncedAt":{"type":"string"}},"required":["success","syncedAt"]}}}},"400":{"description":"Scanner not connected or sync not supported"},"404":{"description":"Connection not found"},"429":{"description":"Scanner sync budget exceeded"},"500":{"description":"Sync failed"}}}},"/scanner-connections/{id}/cases":{"get":{"operationId":"listScannerConnectionCases","description":"Operationally list cases/orders from a scanner connection with pagination, search, and date range filtering. Provider access may idempotently refresh an expired token and record connection health; this endpoint is not a tenant-settings inspection read. Supports Medit, 3Shape, and iTero scanners.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Paginated list of scanner cases/orders","content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"Scanner not connected or invalid query parameters"},"401":{"description":"Unauthorized or token expired"},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"},"502":{"description":"Regional discovery failed"}}}},"/scanner-connections/{id}/cases/{caseId}":{"get":{"operationId":"getScannerConnectionCase","description":"Operationally retrieve detailed information about a specific scanner case/order. Provider access may idempotently refresh an expired token and record connection health; this endpoint is not a tenant-settings inspection read. Supports Medit, 3Shape (searches all active regions), and iTero scanners.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"caseId","in":"path"}],"responses":{"200":{"description":"Scanner case details from the provider","content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"Scanner not connected"},"401":{"description":"Unauthorized or token expired"},"404":{"description":"Connection or case not found"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}/import-case":{"post":{"operationId":"importScannerCase","description":"Import a 3Shape case as a new order. Creates the order, patient (if needed), order items from model elements, and downloads attachments to R2 storage.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"caseId":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:@/-]*$/u"},"regionId":{"type":"integer","minimum":1,"maximum":4},"labId":{"type":"string","minLength":1},"clinicId":{"type":"string","minLength":1},"partnershipId":{"type":"string","minLength":1},"patientId":{"type":"string"}},"required":["caseId","regionId","labId","clinicId","partnershipId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Imported case with order ID, patient, items, and file counts","content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string"},"orderNumber":{"type":"string"},"patientId":{"type":["string","null"]},"orderItemCount":{"type":"number"},"fileCount":{"type":"number"},"threeshapeCaseId":{"type":"string"},"threeshapeOrderNo":{"type":["string","null"]}},"required":["orderId","orderNumber","patientId","orderItemCount","fileCount","threeshapeCaseId","threeshapeOrderNo"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized or token expired"},"404":{"description":"3Shape connection not found"},"409":{"description":"Scanner case has a conflicting destination or an attachment import is already in progress"},"422":{"description":"Scanner case exceeds a local import safety limit"},"429":{"description":"Scanner import rate limit exceeded"},"500":{"description":"Internal server error"},"503":{"description":"One or more scanner attachments remain pending"}}}},"/scanner-connections/{id}/import-files":{"post":{"operationId":"importScannerFiles","description":"Import files from a scanner case into an existing order. Downloads files from the scanner provider (Medit, 3Shape, or iTero) and uploads them to R2 storage. Optionally creates scan inbox items.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"caseUuid":{"type":"string","minLength":1},"fileUuids":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100},"orderId":{"type":"string","minLength":1},"fileRequirementId":{"type":"string"},"treatmentPhaseId":{"type":"string"}},"required":["caseUuid","fileUuids","orderId"],"additionalProperties":false},{"type":"object","properties":{"caseId":{"type":"string","minLength":1},"attachmentHashes":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100},"orderId":{"type":"string","minLength":1},"fileRequirementId":{"type":"string"},"treatmentPhaseId":{"type":"string"},"regionId":{"type":"integer","minimum":1,"maximum":4}},"required":["caseId","attachmentHashes","orderId","regionId"],"additionalProperties":false},{"type":"object","properties":{"rxId":{"type":"string","minLength":1},"downloadTypes":{"type":"array","items":{"type":"string","enum":["ide","thumbnails","screen_capture"]},"minItems":1,"maxItems":3},"orderId":{"type":"string","minLength":1},"fileRequirementId":{"type":"string"},"treatmentPhaseId":{"type":"string"}},"required":["rxId","downloadTypes","orderId"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Import results with success and error counts","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"},"thumbnailUrl":{"type":["string","null"]},"orderId":{"type":"string"},"patientId":{"type":["string","null"]},"fileRequirementId":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","name","type","mimeType","size","url","thumbnailUrl","orderId","patientId","fileRequirementId","createdAt"]}},"errors":{"type":"array","items":{"type":"object","properties":{"fileId":{"type":"string"},"fileName":{"type":"string"},"error":{"type":"string"}},"required":["fileId","fileName","error"]}}},"required":["success","errors"]}}}},"400":{"description":"Invalid request body or scanner not connected"},"401":{"description":"Unauthorized or token expired"},"404":{"description":"Connection, order, or import destination not found"},"409":{"description":"Provider file has a conflicting destination or its import is already in progress"},"429":{"description":"Scanner import rate limit exceeded"},"500":{"description":"Internal server error"},"502":{"description":"Scanner provider returned inconsistent data"}}}},"/scanner-connections/{id}/oauth/authorize":{"post":{"operationId":"initiateScannerOAuth","description":"Initiate the OAuth authorization flow for a scanner connection. Returns the provider-specific authorization URL. Supports Medit, 3Shape (with PKCE), and iTero.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OAuth authorization URL to redirect the user to","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"400":{"description":"OAuth not supported for this scanner type"},"401":{"description":"Unauthorized"},"404":{"description":"Connection not found"},"500":{"description":"Scanner credentials not configured"}}}},"/scanner-connections/{id}/related-accounts":{"get":{"operationId":"listIteroRelatedAccounts","description":"List related iTero accounts available for pairing. Only available for iTero scanner connections that have completed OAuth authentication.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"List of iTero accounts available for pairing","content":{"application/json":{"schema":{"type":"object","properties":{"accounts":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["accounts"]}}}},"400":{"description":"Not an iTero connection or not authenticated"},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}/pair-account":{"post":{"operationId":"pairIteroAccount","description":"Pair an iTero account with this scanner connection and store the paired tokens. Marks the connection as connected upon success.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account successfully paired","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"number"},"accountName":{"type":"string"}},"required":["success","accountId","accountName"]}}}},"400":{"description":"Not an iTero connection, not authenticated, or invalid request"},"404":{"description":"Connection not found"},"500":{"description":"Internal server error"}}}},"/scanner-connections/{id}/webhook-setup":{"post":{"operationId":"setupMeditWebhook","description":"Set up a Medit webhook endpoint for receiving real-time notifications about orders, cases, and files. Replaces any existing webhook endpoint for this connection.","tags":["Scanner Connections"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook registered successfully with endpoint details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"details":{"type":"object","properties":{"endpointUuid":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"url":{"type":"string"}},"required":["endpointUuid","events","url"]}},"required":["success","message","details"]}}}},"400":{"description":"Not a Medit connection or not authorized"},"404":{"description":"Connection not found"},"500":{"description":"Failed to set up webhook"}}}},"/whatsapp-templates":{"get":{"operationId":"listWhatsAppTemplates","description":"List WhatsApp message templates for the current organization with pagination, search, and status filtering.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["draft","pending","approved","rejected"],"example":"approved"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Paginated list of WhatsApp templates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createWhatsAppTemplate","description":"Create a new WhatsApp message template in draft status. The template name is auto-prefixed with the organization slug. Lab-only operation.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"nameSuffix":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z][a-z0-9_]*$"},"displayName":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":500},"language":{"type":"string","minLength":2,"maxLength":2,"pattern":"^[a-z]{2}$","default":"en"},"category":{"type":"string","enum":["UTILITY","MARKETING","AUTHENTICATION"],"default":"UTILITY"},"body":{"type":"string","minLength":1,"maxLength":1024},"variables":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":1600}}},"required":["nameSuffix","displayName","body"]}}}},"responses":{"201":{"description":"Newly created WhatsApp template in draft status","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/whatsapp-templates/sync-all":{"post":{"operationId":"syncAllWhatsAppTemplates","description":"Sync the approval status of all pending WhatsApp templates with Twilio. Updates each template's status to approved, rejected, or keeps as pending. Lab-only operation.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Sync results with status updates for each template","content":{"application/json":{"schema":{"type":"object","properties":{"synced":{"type":"number"},"hasMore":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"error":{"type":"string"}},"required":["id","status"]}}},"required":["synced","hasMore","results"]}}}},"429":{"description":"Template status sync rate limit exceeded"},"500":{"description":"Twilio credentials not configured or internal error"}}}},"/whatsapp-templates/event-overrides":{"get":{"operationId":"listWhatsAppTemplateEventOverrides","description":"List the lab's automatic WhatsApp event template selections and effective platform fallbacks.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Automatic WhatsApp event template selections","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"eventKey":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]},"selectedTemplateId":{"type":["string","null"]},"selectedTemplate":{"type":["object","null"],"properties":{"id":{"type":"string"},"displayName":{"type":"string"},"language":{"type":"string"}},"required":["id","displayName","language"]},"effectiveSource":{"type":"string","enum":["override","platform"]},"fallbackReason":{"type":["string","null"],"enum":["not_configured","template_ineligible","account_disconnected","account_changed",null]},"requiredVariables":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]}},"required":["eventKey","selectedTemplateId","selectedTemplate","effectiveSource","fallbackReason","requiredVariables","accountState"]}}},"required":["data"]}}}},"500":{"description":"Internal server error"}}}},"/whatsapp-templates/event-overrides/{eventKey}/candidates":{"get":{"operationId":"listWhatsAppTemplateEventCandidates","description":"List a bounded page of approved, account-bound templates compatible with one automatic WhatsApp event.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]},"required":true,"name":"eventKey","in":"path"},{"schema":{"type":"integer","minimum":1,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"order received"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"Paginated compatible automatic WhatsApp templates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"displayName":{"type":"string"},"language":{"type":"string","minLength":2,"maxLength":2,"pattern":"^[a-z]{2}$"}},"required":["id","displayName","language"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"500":{"description":"Internal server error"}}}},"/whatsapp-templates/event-overrides/{eventKey}":{"put":{"operationId":"setWhatsAppTemplateEventOverride","description":"Select an approved, account-bound lab template for an automatic WhatsApp event.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]},"required":true,"name":"eventKey","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"templateId":{"type":"string","minLength":1,"maxLength":200}},"required":["templateId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Updated automatic WhatsApp event template selections","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"eventKey":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]},"selectedTemplateId":{"type":["string","null"]},"selectedTemplate":{"type":["object","null"],"properties":{"id":{"type":"string"},"displayName":{"type":"string"},"language":{"type":"string"}},"required":["id","displayName","language"]},"effectiveSource":{"type":"string","enum":["override","platform"]},"fallbackReason":{"type":["string","null"],"enum":["not_configured","template_ineligible","account_disconnected","account_changed",null]},"requiredVariables":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]}},"required":["eventKey","selectedTemplateId","selectedTemplate","effectiveSource","fallbackReason","requiredVariables","accountState"]}}},"required":["data"]}}}},"404":{"description":"Template not found"},"409":{"description":"Template is not eligible for this event or account"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteWhatsAppTemplateEventOverride","description":"Reset an automatic WhatsApp event to the GuideLab platform English template.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]},"required":true,"name":"eventKey","in":"path"}],"responses":{"200":{"description":"Updated automatic WhatsApp event template selections","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"eventKey":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]},"selectedTemplateId":{"type":["string","null"]},"selectedTemplate":{"type":["object","null"],"properties":{"id":{"type":"string"},"displayName":{"type":"string"},"language":{"type":"string"}},"required":["id","displayName","language"]},"effectiveSource":{"type":"string","enum":["override","platform"]},"fallbackReason":{"type":["string","null"],"enum":["not_configured","template_ineligible","account_disconnected","account_changed",null]},"requiredVariables":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]}},"required":["eventKey","selectedTemplateId","selectedTemplate","effectiveSource","fallbackReason","requiredVariables","accountState"]}}},"required":["data"]}}}},"500":{"description":"Internal server error"}}}},"/whatsapp-templates/{id}":{"get":{"operationId":"getWhatsAppTemplate","description":"Retrieve a single WhatsApp template by ID, including its body, variables, approval status, and Twilio content SID.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"WhatsApp template details","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]}},"required":["template"]}}}},"404":{"description":"Template not found"},"500":{"description":"Internal server error"}}},"put":{"operationId":"updateWhatsAppTemplate","description":"Update a WhatsApp template's name, body, variables, or other properties. Only draft templates can be edited. Lab-only operation.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"nameSuffix":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z][a-z0-9_]*$"},"displayName":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":500},"language":{"type":"string","minLength":2,"maxLength":2,"pattern":"^[a-z]{2}$"},"category":{"type":"string","enum":["UTILITY","MARKETING","AUTHENTICATION"]},"body":{"type":"string","minLength":1,"maxLength":1024},"variables":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":1600}}}}}}},"responses":{"200":{"description":"Successfully updated WhatsApp template","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Template is not in draft status or invalid request body"},"404":{"description":"Template not found"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteWhatsAppTemplate","description":"Permanently delete a WhatsApp template and its event selections, durably recovering exact-account Twilio cleanup when needed. Lab-only operation.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Template successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Template not found"},"409":{"description":"The exact legacy Twilio account is unavailable"},"429":{"description":"Template deletion budget exceeded"},"500":{"description":"Internal server error"}}}},"/whatsapp-templates/{id}/register":{"post":{"operationId":"registerWhatsAppTemplate","description":"Register a draft WhatsApp template with Twilio for WhatsApp approval. Creates the content in Twilio, submits for approval, and updates the template status to pending. Lab-only operation.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Template registered and submitted for WhatsApp approval","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]},"providerOperation":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"string","enum":["registered","pending"]}},"required":["id","state"]}},"required":["template","providerOperation"]}}}},"202":{"description":"Template registration durably accepted for bounded recovery","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]},"providerOperation":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"string","enum":["registered","pending"]}},"required":["id","state"]}},"required":["template","providerOperation"]}}}},"400":{"description":"Template is not in draft status"},"404":{"description":"Template not found"},"409":{"description":"Lab Twilio integration is unavailable or manual review is required","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},{"type":"object","properties":{"error":{"type":"string"},"providerOperation":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"string","enum":["manual_review","failed"]}},"required":["id","state"]}},"required":["error","providerOperation"]}]}}}},"429":{"description":"Template registration budget exceeded"},"500":{"description":"Twilio credentials not configured or registration failed"}}}},"/whatsapp-templates/{id}/sync":{"post":{"operationId":"syncWhatsAppTemplate","description":"Sync the approval status of a single WhatsApp template with Twilio. Requires the template to have been registered (has a contentSid).","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Template with updated approval status from Twilio","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":"string"},"category":{"type":"string"},"contentType":{"type":"string"},"body":{"type":"string"},"variables":{"type":["object","null"],"additionalProperties":{"type":"string","minLength":1,"maxLength":1600}},"status":{"type":"string"},"contentSid":{"type":["string","null"]},"rejectionReason":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"]},"lastSyncError":{"type":["string","null"]},"usageCount":{"type":["number","null"],"description":"Deprecated compatibility count; advances only for provider-accepted custom-template sends. Prefer successfulSendCount."},"successfulSendCount":{"type":"number"},"providerScope":{"type":"string","enum":["unbound","platform_legacy","lab_account"]},"accountState":{"type":"string","enum":["unbound","platform","connected","disconnected","changed"]},"providerOperation":{"type":["object","null"],"properties":{"id":{"type":"string"},"phase":{"type":"string","enum":["ready_create","create_ambiguous","ready_submit","ready_delete","manual_review"]},"state":{"type":"string","enum":["pending","manual_review"]}},"required":["id","phase","state"]},"compatibleEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"selectedEvents":{"type":"array","items":{"type":"string","enum":["order_received_lab","order_submitted_clinic"]}},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","displayName","description","language","category","contentType","body","variables","status","contentSid","rejectionReason","lastSyncedAt","lastSyncError","usageCount","successfulSendCount","providerScope","accountState","providerOperation","compatibleEvents","selectedEvents","createdBy","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Template has not been registered with Twilio"},"404":{"description":"Template not found"},"409":{"description":"Template's Twilio account is unavailable or changed"},"429":{"description":"Template status provider-read budget exceeded"},"500":{"description":"Twilio credentials not configured or internal error"}}}},"/whatsapp-templates/{id}/test":{"post":{"operationId":"testWhatsAppTemplate","description":"Send a test WhatsApp message using an approved template and the exact Twilio account that owns its Content SID. Lab-only operation.","tags":["WhatsApp Templates"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","minLength":1,"pattern":"^\\+[1-9]\\d{6,14}$"},"variables":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":1600}}},"required":["phoneNumber"]}}}},"responses":{"200":{"description":"Test message accepted for processing","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Template not approved or invalid request body"},"404":{"description":"Template not found"},"409":{"description":"Template's Twilio account is unavailable or changed"},"429":{"description":"Paid test-message rate limit exceeded"},"500":{"description":"Twilio credentials not configured or send failed"},"503":{"description":"Durable WhatsApp queue is unavailable"}}}},"/scan-inbox":{"get":{"tags":["Scan Inbox"],"operationId":"listScanInboxItems","summary":"List scan inbox items","description":"Retrieve paginated scan inbox items for the current scanner-owning organization. Supports filtering by status, source, and search term, with configurable sorting.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"example":20},"required":false,"name":"pageSize","in":"query"},{"schema":{"type":"string","example":"new"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"3shape"},"required":false,"name":"source","in":"query"},{"schema":{"type":"string","enum":["createdAt","scanDate","dueDate","patientName"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of scan inbox items","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"},"totalPages":{"type":"number"}},"required":["total","page","pageSize","totalPages"]}},"required":["items","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized"}}}},"/scan-inbox/sync":{"post":{"tags":["Scan Inbox"],"operationId":"syncScanInbox","summary":"Sync scan inbox from scanner connections","description":"Trigger a sync of scan inbox items from all configured scanner connections. Optionally specify a single connectionId to sync. Automatically attempts to match new items to tenant-visible orders and patients.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"connectionId":{"type":"string"}}}}}},"responses":{"200":{"description":"Sync results with count of new items imported","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string"},"newItems":{"type":"number"}},"required":["newItems"]}},"totalNewItems":{"type":"number"}},"required":["results","totalNewItems"]}}}},"400":{"description":"Invalid sync request"},"401":{"description":"Unauthorized"},"422":{"description":"Sync request exceeds a local safety limit"},"429":{"description":"Scanner sync budget exceeded"}}}},"/scan-inbox/bulk":{"post":{"tags":["Scan Inbox"],"operationId":"bulkScanInboxAction","summary":"Bulk action on scan inbox items","description":"Perform a bulk action (currently 'dismiss') on up to 100 scan inbox items owned by the current organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["dismiss"]},"ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":100},"reason":{"type":"string"}},"required":["action","ids"]}}}},"responses":{"200":{"description":"Bulk action completed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"action":{"type":"string"},"count":{"type":"number"}},"required":["success","action","count"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"}}}},"/scan-inbox/{id}/assign":{"post":{"tags":["Scan Inbox"],"operationId":"assignScanInboxItem","summary":"Assign scan inbox item to existing order","description":"Assign a scan inbox item to an existing order. Downloads attachments from the scanner (e.g. 3Shape) and uploads them as order files. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string"}},"required":["orderId"]}}}},"responses":{"200":{"description":"Item assigned to order with imported file count","content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string"},"orderNumber":{"type":"string"},"fileCount":{"type":"number"}},"required":["orderId","orderNumber","fileCount"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"404":{"description":"Scan inbox item or order not found"},"409":{"description":"Scan inbox item was handled by another request or its file import is already in progress"},"503":{"description":"One or more scanner attachments remain pending"}}}},"/scan-inbox/{id}/book":{"post":{"tags":["Scan Inbox"],"operationId":"bookScanInboxItem","summary":"Book scan inbox item as new order","description":"Create a new draft order from a scan inbox item. Optionally creates a patient record and order items from scan metadata. Downloads scanner attachments as order files. Lab only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"clinicId":{"type":"string","minLength":1},"partnershipId":{"type":"string","minLength":1},"patientId":{"type":"string","minLength":1}},"additionalProperties":false}}}},"responses":{"201":{"description":"New order created from scan inbox item","content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string"},"orderNumber":{"type":"string"},"patientId":{"type":["string","null"]},"orderItemCount":{"type":"number"},"fileCount":{"type":"number"}},"required":["orderId","orderNumber","patientId","orderItemCount","fileCount"]}}}},"400":{"description":"Invalid booking destination"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - lab only"},"404":{"description":"Scan inbox item not found"},"409":{"description":"Scan inbox item is already being handled"},"422":{"description":"Scanner case exceeds a local import safety limit"},"429":{"description":"Scanner import rate limit exceeded"},"503":{"description":"One or more scanner attachments remain pending"}}}},"/scan-inbox/{id}/dismiss":{"post":{"tags":["Scan Inbox"],"operationId":"dismissScanInboxItem","summary":"Dismiss a scan inbox item","description":"Mark an organization-owned scan inbox item as dismissed with an optional reason. The item will no longer appear in active scan inbox lists.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Scan inbox item dismissed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Scan inbox item not found"},"409":{"description":"Scan inbox item is no longer available or its file import is already in progress"}}}},"/scan-inbox/{id}/match":{"post":{"tags":["Scan Inbox"],"operationId":"matchScanInboxItem","summary":"Re-run matching for a scan inbox item","description":"Trigger the matching algorithm on a scan inbox item to find tenant-visible order, patient, or clinic matches. Updates match metadata and may auto-promote status to 'matched' if confidence is high enough.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Match result with confidence score and matched entities","content":{"application/json":{"schema":{"type":"object","properties":{"matchedOrderId":{"type":["string","null"]},"matchedOrderLabId":{"type":["string","null"]},"matchedPatientId":{"type":["string","null"]},"matchedClinicId":{"type":["string","null"]},"confidence":{"type":"number"},"matchType":{"type":["string","null"]},"reason":{"type":["string","null"]},"status":{"type":"string"}},"required":["matchedOrderId","matchedOrderLabId","matchedPatientId","matchedClinicId","confidence","matchType","reason","status"]}}}},"401":{"description":"Unauthorized"},"404":{"description":"Scan inbox item not found"}}}},"/integrations/xero":{"get":{"operationId":"getXeroStatus","description":"Retrieve the current Xero integration status, connection state, and configuration for the authenticated lab.","tags":["Integrations"],"summary":"Get Xero integration status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Xero integration status including connection state and sync settings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"isConnected":{"type":"boolean"},"tenantName":{"type":["string","null"]},"config":{}},"required":["id","isEnabled","isConnected","tenantName"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"updateXeroSettings","description":"Update Xero integration settings such as default sales account, tax type, bank account, and sync preferences. Requires organization manager access.","tags":["Integrations"],"summary":"Update Xero integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Updated Xero integration status and configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"isConnected":{"type":"boolean"},"tenantName":{"type":["string","null"]},"config":{}},"required":["id","isEnabled","isConnected","tenantName"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/authorize":{"post":{"operationId":"initiateXeroOAuth","description":"Initiate the Xero OAuth 2.0 authorization flow. Returns a URL to redirect the user to Xero's consent screen. Requires organization manager access.","tags":["Integrations"],"summary":"Initiate Xero OAuth authorization","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Xero OAuth consent URL to redirect the user to","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/callback":{"get":{"operationId":"handleXeroOAuthCallback","description":"Handle the Xero OAuth 2.0 callback after user authorization. Exchanges the authorization code for tokens and stores them.","tags":["Integrations"],"summary":"Handle Xero OAuth callback","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Redirect to integration settings page with connection status","content":{"application/json":{"schema":{}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/disconnect":{"post":{"operationId":"disconnectXero","description":"Disconnect the Xero integration by clearing OAuth tokens while preserving configuration settings. Requires organization manager access.","tags":["Integrations"],"summary":"Disconnect Xero integration","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Xero integration successfully disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/accounts":{"get":{"operationId":"listXeroAccounts","description":"Fetch chart of accounts from the connected Xero organization for settings configuration. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token. Supports filtering by account type (revenue or bank).","tags":["Integrations"],"summary":"List Xero accounts","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of Xero accounts with ID, code, name, type, and tax type","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"taxType":{"type":["string","null"]}},"required":["accountId","code","name","type","taxType"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/tax-rates":{"get":{"operationId":"listXeroTaxRates","description":"Fetch available tax rates from the connected Xero organization for settings configuration. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token.","tags":["Integrations"],"summary":"List Xero tax rates","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of Xero tax rates with name, type, and effective rate","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"taxType":{"type":"string"},"effectiveRate":{"type":"number"}},"required":["name","taxType","effectiveRate"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/contacts/search":{"get":{"operationId":"searchXeroContacts","description":"Search for contacts in the connected Xero organization while configuring mappings. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token. Requires a minimum 2-character query.","tags":["Integrations"],"summary":"Search Xero contacts","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of matching Xero contacts with ID, name, and email","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"contactId":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]}},"required":["contactId","name","email"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/contact-mappings":{"get":{"operationId":"listXeroContactMappings","description":"Retrieve all clinic-to-Xero contact mappings for the authenticated lab.","tags":["Integrations"],"summary":"List Xero contact mappings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of clinic-to-Xero contact mappings with match method","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"upsertXeroContactMapping","description":"Create or update a mapping between a partner clinic and a Xero contact for invoice synchronization. Requires organization manager access.","tags":["Integrations"],"summary":"Upsert Xero contact mapping","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Contact mapping successfully created or updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"delete":{"operationId":"deleteXeroContactMapping","description":"Remove a clinic-to-Xero contact mapping by clinic ID. Requires organization manager access.","tags":["Integrations"],"summary":"Delete Xero contact mapping","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Contact mapping successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/sync-invoice":{"post":{"operationId":"syncXeroInvoice","description":"Enqueue a single invoice for synchronization to Xero. Sets the invoice sync status to pending and adds it to the sync queue.","tags":["Integrations"],"summary":"Sync an invoice to Xero","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Invoice sync enqueued successfully with pending status","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"xeroSyncStatus":{"type":"string"}},"required":["success","xeroSyncStatus"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/xero/sync-payment":{"post":{"operationId":"syncXeroPayment","description":"Replay or reconcile one payment allocation against the active Xero account.","tags":["Integrations"],"summary":"Sync a payment allocation to Xero","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Payment sync accepted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Xero integration is not active"},"404":{"description":"Payment allocation not found"},"409":{"description":"Payment is bound elsewhere or retry budget exhausted"}}}},"/integrations/xero/sync-log":{"get":{"operationId":"listXeroSyncLogs","description":"Retrieve Xero synchronization log entries for the authenticated lab. Supports filtering by entity type and entity ID.","tags":["Integrations"],"summary":"List Xero sync logs","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of Xero sync log entries ordered by most recent first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/twilio":{"get":{"operationId":"getTwilioStatus","description":"Retrieve the current Twilio integration status and configuration for the authenticated lab. Auth tokens are masked in the response.","tags":["Integrations"],"summary":"Get Twilio integration status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Twilio integration status and masked configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{}},"required":["id","isEnabled"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"updateTwilioIntegration","description":"Create or update the Twilio integration settings including account credentials and phone numbers. Requires organization manager access.","tags":["Integrations"],"summary":"Update Twilio integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Updated Twilio integration status and masked configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{}},"required":["id","isEnabled"]}}}},"400":{"description":"Invalid Twilio integration configuration"},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"Stored Twilio integration configuration is invalid"}}}},"/integrations/twilio/test-connection":{"post":{"operationId":"testTwilioConnection","description":"Verify Twilio API credentials by making a test call to the Twilio API. Returns whether the credentials are valid.","tags":["Integrations"],"summary":"Test Twilio API credentials","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Connection test result with validity status","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"friendlyName":{"type":"string"},"status":{"type":"string"},"error":{"type":"string"}},"required":["valid"]}}}},"400":{"description":"Invalid Twilio credentials"},"401":{"description":"Unauthorized - valid session required"},"429":{"description":"Twilio connection-test rate limit exceeded"}}}},"/integrations/twilio/test-message":{"post":{"operationId":"sendTwilioTestMessage","description":"Queue a test WhatsApp or SMS message through the authenticated lab's exact saved Twilio account and channel sender.","tags":["Integrations"],"summary":"Send a test message via Twilio","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Test message queue admission result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"}},"required":["success"]}}}},"400":{"description":"Invalid test-message request"},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"Saved Twilio credentials or sender are unavailable"},"429":{"description":"Paid test-message rate limit exceeded"},"503":{"description":"Test-message queue or credentials temporarily unavailable"}}}},"/integrations/dentally":{"get":{"operationId":"getDentallyConnection","description":"Retrieve the current Dentally connection status and configuration for the authenticated clinic.","tags":["Integrations"],"summary":"Get Dentally connection status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Dentally connection details or null if not configured","content":{"application/json":{"schema":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"apiBaseUrl":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"syncEnabled":{"type":"boolean"},"lastSyncAt":{"type":["string","null"]},"lastSyncStatus":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","apiBaseUrl","connectionStatus","statusMessage","syncEnabled","lastSyncAt","lastSyncStatus","isActive","createdAt","updatedAt"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"post":{"operationId":"connectDentally","description":"Create or update a Dentally connection for the authenticated clinic. Validates credentials before saving. Requires organization manager access.","tags":["Integrations"],"summary":"Connect or update Dentally integration","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"string","minLength":1},"apiBaseUrl":{"type":"string","enum":["https://api.dentally.co","https://api.apac.dentally.com","https://api.ca.dentally.com","https://api.sandbox.dentally.co"]},"name":{"type":"string","minLength":1,"maxLength":100,"default":"Main Practice"}},"required":["apiKey","apiBaseUrl"],"additionalProperties":false}}}},"responses":{"200":{"description":"Created or updated Dentally connection details","content":{"application/json":{"schema":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"apiBaseUrl":{"type":"string"},"connectionStatus":{"type":"string"},"statusMessage":{"type":["string","null"]},"syncEnabled":{"type":"boolean"},"lastSyncAt":{"type":["string","null"]},"lastSyncStatus":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","apiBaseUrl","connectionStatus","statusMessage","syncEnabled","lastSyncAt","lastSyncStatus","isActive","createdAt","updatedAt"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"delete":{"operationId":"disconnectDentally","description":"Remove the Dentally connection for the authenticated clinic. Requires organization manager access.","tags":["Integrations"],"summary":"Disconnect Dentally integration","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Dentally connection successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/dentally/sync":{"post":{"operationId":"triggerDentallySync","description":"Trigger a patient sync from Dentally to the local database for the authenticated clinic.","tags":["Integrations"],"summary":"Trigger Dentally patient sync","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Sync result with counts of created, updated, and skipped patients","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"syncType":{"type":"string"},"status":{"type":"string"},"patientsCreated":{"type":"number"},"patientsUpdated":{"type":"number"},"patientsSkipped":{"type":"number"},"errorCount":{"type":"number"},"errors":{},"durationMs":{"type":["number","null"]},"triggeredBy":{"type":["string","null"]},"startedAt":{"type":"string"},"completedAt":{"type":["string","null"]}},"required":["id","syncType","status","patientsCreated","patientsUpdated","patientsSkipped","errorCount","durationMs","triggeredBy","startedAt","completedAt"]}}}},"401":{"description":"Unauthorized - valid session required"},"429":{"description":"Dentally sync rate limit exceeded"}}}},"/integrations/dentally/sync-logs":{"get":{"operationId":"listDentallySyncLogs","description":"Retrieve recent Dentally patient sync log entries for the authenticated clinic.","tags":["Integrations"],"summary":"List Dentally sync logs","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Array of sync log entries ordered by most recent first","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"syncType":{"type":"string"},"status":{"type":"string"},"patientsCreated":{"type":"number"},"patientsUpdated":{"type":"number"},"patientsSkipped":{"type":"number"},"errorCount":{"type":"number"},"errors":{},"durationMs":{"type":["number","null"]},"triggeredBy":{"type":["string","null"]},"startedAt":{"type":"string"},"completedAt":{"type":["string","null"]}},"required":["id","syncType","status","patientsCreated","patientsUpdated","patientsSkipped","errorCount","durationMs","triggeredBy","startedAt","completedAt"]}}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/dentally/test":{"post":{"operationId":"testDentallyConnection","description":"Test Dentally API credentials without saving them. Returns whether the connection is valid.","tags":["Integrations"],"summary":"Test Dentally API credentials","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"string","minLength":1},"apiBaseUrl":{"type":"string","enum":["https://api.dentally.co","https://api.apac.dentally.com","https://api.ca.dentally.com","https://api.sandbox.dentally.co"]}},"required":["apiKey","apiBaseUrl"],"additionalProperties":false}}}},"responses":{"200":{"description":"Connection test result with validity status and message","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"message":{"type":"string"}},"required":["valid"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/ariba":{"get":{"operationId":"getAribaStatus","description":"Retrieve the current SAP Ariba integration status and full configuration (connection, accounting, billing, automation) for the authenticated lab.","tags":["Integrations"],"summary":"Get Ariba integration status and configuration","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Ariba integration status with masked secrets and full configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{"type":"object","properties":{"connection":{},"accounting":{},"billTo":{},"automation":{}}}},"required":["id","isEnabled","config"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"updateAribaIntegration","description":"Create or update the SAP Ariba integration settings including connection, accounting, billing, and automation configuration. Requires organization manager access.","tags":["Integrations"],"summary":"Update Ariba integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isEnabled":{"type":"boolean"},"config":{"type":"object","properties":{"connection":{"type":"object","properties":{"deploymentMode":{"type":"string","enum":["test","production"],"default":"test"},"senderNetworkId":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"recipientNetworkId":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"sharedSecret":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"endpointUrl":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"uri"}]},"contractNumber":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]}},"additionalProperties":false},"accounting":{"type":"object","properties":{"company":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"percentage":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"businessUnit":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"costCentre":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"procurementUnit":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"account":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]}},"additionalProperties":false},"billTo":{"type":"object","properties":{"addressId":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"name":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"street":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"city":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"state":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"postalCode":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"country":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]}},"additionalProperties":false},"automation":{"type":"object","properties":{"autoSendXml":{"type":"boolean","default":false}},"additionalProperties":false}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated Ariba integration status and configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{"type":"object","properties":{"connection":{},"accounting":{},"billTo":{},"automation":{}}}},"required":["id","isEnabled","config"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/ariba/preview-cxml":{"get":{"operationId":"previewAribaCxml","description":"Generate a preview of the cXML invoice document that would be sent to SAP Ariba for the specified invoice.","tags":["Integrations"],"summary":"Preview Ariba cXML invoice","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Generated cXML invoice document preview","content":{"application/json":{"schema":{"type":"object","properties":{"cxml":{"type":"string"}},"required":["cxml"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/ariba/send-invoice":{"post":{"operationId":"sendAribaInvoice","description":"Build and transmit a cXML invoice to the configured SAP Ariba endpoint for the specified invoice. Requires organization manager access.","tags":["Integrations"],"summary":"Send invoice to Ariba via cXML","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"invoiceId":{"type":"string","minLength":1}},"required":["invoiceId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Ariba transmission result with HTTP status and response body","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"statusCode":{"type":"number"},"response":{"type":"string"}},"required":["success","statusCode","response"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/exocad":{"get":{"operationId":"getExocadStatus","description":"Retrieve the current exocad integration status, configuration, and all material/reconstruction type mappings for the authenticated lab.","tags":["Integrations"],"summary":"Get exocad integration status and mappings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Exocad integration status with configuration and mappings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{},"reconstructionMappings":{"type":"array","items":{},"maxItems":500},"materialMappings":{"type":"array","items":{},"maxItems":500}},"required":["id","isEnabled","reconstructionMappings","materialMappings"]}}}},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"Stored Exocad mapping capacity is invalid"}}},"put":{"operationId":"updateExocadIntegration","description":"Create or update the exocad integration settings including folder paths and auto-import configuration for the authenticated lab.","tags":["Integrations"],"summary":"Update exocad integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Updated exocad integration record","content":{"application/json":{"schema":{"type":"object","properties":{"integration":{}}}}}},"400":{"description":"Invalid integration configuration"},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"Stored integration configuration is invalid"}}}},"/integrations/exocad/material-mappings":{"put":{"operationId":"updateExocadMaterialMappings","description":"Update the mapping between exocad material names and local material records for the authenticated lab.","tags":["Integrations"],"summary":"Update exocad material mappings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"exocadMaterial":{"type":"string","minLength":1,"maxLength":200},"materialId":{"type":["string","null"],"minLength":1,"maxLength":200}},"required":["exocadMaterial","materialId"]},"maxItems":100}},"required":["mappings"]}}}},"responses":{"200":{"description":"Updated list of exocad material mappings","content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exocadMaterial":{"type":"string"},"materialId":{"type":["string","null"]}},"required":["id","exocadMaterial","materialId"]},"maxItems":500}},"required":["mappings"]}}}},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"Exocad mapping capacity reached"}}}},"/integrations/exocad/reconstruction-mappings":{"put":{"operationId":"updateExocadReconstructionMappings","description":"Update the mapping between exocad reconstruction types and local product records for the authenticated lab.","tags":["Integrations"],"summary":"Update exocad reconstruction type mappings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"exocadType":{"type":"string","minLength":1,"maxLength":200},"productId":{"type":["string","null"],"minLength":1,"maxLength":200}},"required":["exocadType","productId"]},"maxItems":100}},"required":["mappings"]}}}},"responses":{"200":{"description":"Updated list of exocad reconstruction type mappings","content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exocadType":{"type":"string"},"productId":{"type":["string","null"]}},"required":["id","exocadType","productId"]},"maxItems":500}},"required":["mappings"]}}}},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"Exocad mapping capacity reached"}}}},"/integrations/bite-finder":{"get":{"operationId":"getBiteFinderStatus","description":"Retrieve the current Bite Finder integration status and configuration for the authenticated lab.","tags":["Integrations"],"summary":"Get Bite Finder integration status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Bite Finder integration status and masked configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{}},"required":["id","isEnabled"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"updateBiteFinderIntegration","description":"Create or update the Bite Finder integration settings for the authenticated lab. Requires organization manager access.","tags":["Integrations"],"summary":"Update Bite Finder integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isEnabled":{"type":"boolean"},"config":{"type":"object","properties":{"apiKey":{"type":"string","minLength":1},"endpointUrl":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"uri"}]}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated Bite Finder integration status and masked configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{}},"required":["id","isEnabled"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/heygears":{"get":{"operationId":"getHeyGearsStatus","description":"Retrieve the current HeyGears integration status and configuration for the authenticated lab.","tags":["Integrations"],"summary":"Get HeyGears integration status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"HeyGears integration status and masked configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{}},"required":["id","isEnabled"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"updateHeyGearsIntegration","description":"Create or update the HeyGears integration settings for the authenticated lab. Requires organization manager access.","tags":["Integrations"],"summary":"Update HeyGears integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isEnabled":{"type":"boolean"},"config":{"type":"object","properties":{"apiKey":{"type":"string","minLength":1},"endpointUrl":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"uri"}]}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated HeyGears integration status and masked configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"config":{}},"required":["id","isEnabled"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/ship-engine":{"get":{"operationId":"getShipEngineIntegration","tags":["Integrations"],"summary":"Get ShipStation partner shipping status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Shipping integration status","content":{"application/json":{"schema":{}}}}}}},"/integrations/ship-engine/provision":{"post":{"operationId":"provisionShipEngineIntegration","tags":["Integrations"],"summary":"Provision the lab's ShipStation partner sub-account","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"originCountryCode":{"type":"string","enum":["GB","IT"]}},"required":["originCountryCode"],"additionalProperties":false}}}},"responses":{"200":{"description":"Existing connected account"},"201":{"description":"Sub-account and tracking webhook provisioned"},"202":{"description":"A bounded provisioning attempt is already in progress"},"400":{"description":"Unsupported or mismatched origin country"},"403":{"description":"Organization manager access required"},"429":{"description":"Provider operation budget exhausted"},"502":{"description":"Provider setup failed safely"},"503":{"description":"Provider integration is disabled"}}}},"/integrations/ship-engine/carriers":{"get":{"operationId":"listShipEngineCarriers","tags":["Integrations"],"summary":"List the lab's connected carrier accounts","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Bounded carrier list"},"409":{"description":"Shipping provider is not connected"},"429":{"description":"Provider operation budget exhausted"}}},"post":{"operationId":"connectShipEngineCarrier","tags":["Integrations"],"summary":"Connect a lab-owned carrier account without retaining its credentials","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"carrierName":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[a-z][a-z0-9_]*$/u"},"credentials":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","maxLength":4096},{"type":"number"},{"type":"boolean"}]}}},"required":["carrierName","credentials"],"additionalProperties":false}}}},"responses":{"201":{"description":"Carrier account connected"},"409":{"description":"Shipping provider is not connected"},"429":{"description":"Provider operation budget exhausted"},"502":{"description":"Carrier connection rejected"}}}},"/integrations/fatture-in-cloud":{"get":{"operationId":"getFattureInCloudStatus","description":"Retrieve the current Fatture in Cloud integration status, connection state, and configuration for the authenticated lab.","tags":["Integrations"],"summary":"Get Fatture in Cloud integration status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Fatture in Cloud integration status including connection state and settings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"connectionState":{"type":"string","enum":["disconnected","pending_company_selection","connected"]},"companyName":{"type":["string","null"]},"config":{"type":"object","properties":{"numeration":{"type":"string"},"defaultVatTypeId":{"type":["integer","null"]},"paymentAccountId":{"type":["integer","null"]},"paymentMethodId":{"type":["integer","null"]},"autoSyncOnSend":{"type":"boolean"},"autoSyncPayments":{"type":"boolean"},"sdiEnabled":{"type":"boolean"}},"required":["numeration","defaultVatTypeId","paymentAccountId","paymentMethodId","autoSyncOnSend","autoSyncPayments","sdiEnabled"]}},"required":["id","isEnabled","connectionState","companyName","config"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"updateFattureInCloudSettings","description":"Update Fatture in Cloud integration settings such as numeration, default VAT type, payment account/method, sync preferences, and the SDI toggle. Requires organization manager access.","tags":["Integrations"],"summary":"Update Fatture in Cloud integration settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Updated Fatture in Cloud integration status and configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"connectionState":{"type":"string","enum":["disconnected","pending_company_selection","connected"]},"companyName":{"type":["string","null"]},"config":{"type":"object","properties":{"numeration":{"type":"string"},"defaultVatTypeId":{"type":["integer","null"]},"paymentAccountId":{"type":["integer","null"]},"paymentMethodId":{"type":["integer","null"]},"autoSyncOnSend":{"type":"boolean"},"autoSyncPayments":{"type":"boolean"},"sdiEnabled":{"type":"boolean"}},"required":["numeration","defaultVatTypeId","paymentAccountId","paymentMethodId","autoSyncOnSend","autoSyncPayments","sdiEnabled"]}},"required":["id","isEnabled","connectionState","companyName","config"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/authorize":{"post":{"operationId":"initiateFattureInCloudOAuth","description":"Initiate the Fatture in Cloud OAuth 2.0 authorization flow. Returns a URL to redirect the user to the consent screen. Requires organization manager access.","tags":["Integrations"],"summary":"Initiate Fatture in Cloud OAuth authorization","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Fatture in Cloud OAuth consent URL to redirect the user to","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"401":{"description":"Unauthorized - valid session required"},"403":{"description":"Organization manager access required"},"409":{"description":"The organization currency is not EUR (code: currency_not_supported)"},"503":{"description":"Fatture in Cloud OAuth is not configured"}}}},"/integrations/fatture-in-cloud/callback":{"get":{"operationId":"handleFattureInCloudOAuthCallback","description":"Handle the Fatture in Cloud OAuth 2.0 callback. Exchanges the authorization code for tokens, and either binds the single visible company or parks the grant until a manager selects one.","tags":["Integrations"],"summary":"Handle Fatture in Cloud OAuth callback","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"302":{"description":"Redirect back to the integration settings page carrying the connection status in the fatture_in_cloud query parameter"},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/companies":{"get":{"operationId":"listFattureInCloudCompanies","description":"List the Fatture in Cloud companies visible to the pending grant so a manager can select exactly one. Requires organization manager access.","tags":["Integrations"],"summary":"List Fatture in Cloud companies","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Companies available to the pending authorization","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"vatNumber":{"type":["string","null"]},"taxCode":{"type":["string","null"]}},"required":["id","name","vatNumber","taxCode"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/select-company":{"post":{"operationId":"selectFattureInCloudCompany","description":"Bind the pending Fatture in Cloud grant to exactly one company, activating the integration. Requires organization manager access.","tags":["Integrations"],"summary":"Select the Fatture in Cloud company","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Company bound and integration activated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/disconnect":{"post":{"operationId":"disconnectFattureInCloud","description":"Disconnect the Fatture in Cloud integration by revoking the OAuth grant while preserving configuration settings. Requires organization manager access.","tags":["Integrations"],"summary":"Disconnect Fatture in Cloud integration","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Fatture in Cloud integration successfully disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/vat-types":{"get":{"operationId":"listFattureInCloudVatTypes","description":"Fetch VAT types from the connected Fatture in Cloud company for settings configuration. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token.","tags":["Integrations"],"summary":"List Fatture in Cloud VAT types","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of VAT types with id, percentage, and description","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"value":{"type":"number"},"description":{"type":["string","null"]},"eiType":{"type":["string","null"]}},"required":["id","value","description","eiType"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/payment-accounts":{"get":{"operationId":"listFattureInCloudPaymentAccounts","description":"Fetch payment accounts from the connected Fatture in Cloud company for settings configuration. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token.","tags":["Integrations"],"summary":"List Fatture in Cloud payment accounts","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of payment accounts with id, name, and type","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string"},"type":{"type":["string","null"]}},"required":["id","name","type"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/payment-methods":{"get":{"operationId":"listFattureInCloudPaymentMethods","description":"Fetch payment methods from the connected Fatture in Cloud company for settings configuration. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token.","tags":["Integrations"],"summary":"List Fatture in Cloud payment methods","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of payment methods with id and name","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/clients/search":{"get":{"operationId":"searchFattureInCloudClients","description":"Search for clients in the connected Fatture in Cloud company while configuring mappings. Owner/admin only; provider access may idempotently refresh and persist an expired OAuth token. Requires a minimum 2-character query.","tags":["Integrations"],"summary":"Search Fatture in Cloud clients","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of matching clients with id, name, VAT number, and tax code","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"clientId":{"type":"string"},"name":{"type":"string"},"vatNumber":{"type":["string","null"]},"taxCode":{"type":["string","null"]}},"required":["clientId","name","vatNumber","taxCode"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/client-mappings":{"get":{"operationId":"listFattureInCloudClientMappings","description":"Retrieve all clinic-to-Fatture in Cloud client mappings for the authenticated lab.","tags":["Integrations"],"summary":"List Fatture in Cloud client mappings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of clinic-to-client mappings with match method","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"partnershipId":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"providerClientId":{"type":"string"},"providerClientName":{"type":"string"},"vatNumber":{"type":["string","null"]},"taxCode":{"type":["string","null"]},"matchMethod":{"type":"string"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","partnershipId","clinicId","clinicName","providerClientId","providerClientName","vatNumber","taxCode","matchMethod","updatedAt"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"operationId":"upsertFattureInCloudClientMapping","description":"Create or update a mapping between a partner clinic and a Fatture in Cloud client for document synchronization. Requires organization manager access.","tags":["Integrations"],"summary":"Upsert Fatture in Cloud client mapping","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Client mapping successfully created or updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"delete":{"operationId":"deleteFattureInCloudClientMapping","description":"Remove a clinic-to-Fatture in Cloud client mapping by clinic ID. Requires organization manager access.","tags":["Integrations"],"summary":"Delete Fatture in Cloud client mapping","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Client mapping successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/sync-invoice":{"post":{"operationId":"syncFattureInCloudInvoice","description":"Enqueue a single invoice for export to Fatture in Cloud. The provider assigns the fiscal number inside the configured numeration.","tags":["Integrations"],"summary":"Sync an invoice to Fatture in Cloud","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Invoice export enqueued successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"syncStatus":{"type":"string"}},"required":["success","syncStatus"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/sync-credit-note":{"post":{"operationId":"syncFattureInCloudCreditNote","description":"Enqueue a single credit note for export to Fatture in Cloud, or replay one that reached a terminal state.","tags":["Integrations"],"summary":"Sync a credit note to Fatture in Cloud","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Credit note export enqueued successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"syncStatus":{"type":"string"}},"required":["success","syncStatus"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/integrations/fatture-in-cloud/sync-payments":{"post":{"operationId":"syncFattureInCloudPayments","description":"Reconcile the embedded payment schedule of one exported invoice against the connected Fatture in Cloud company.","tags":["Integrations"],"summary":"Reconcile invoice payments to Fatture in Cloud","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Payment reconciliation accepted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Fatture in Cloud integration is not active"},"409":{"description":"Invoice is not exported or retry budget exhausted"}}}},"/integrations/fatture-in-cloud/send-e-invoice":{"post":{"operationId":"sendFattureInCloudEInvoice","description":"Explicitly submit one exported invoice to SDI through Fatture in Cloud after verification. SDI submission is a legal act: it only ever happens through this manager-confirmed action, never automatically.","tags":["Integrations"],"summary":"Send an invoice to SDI","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"SDI submission enqueued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Fatture in Cloud integration is not active"},"409":{"description":"SDI is not enabled, the invoice is not exported, or the send already happened"}}}},"/integrations/fatture-in-cloud/refresh-e-invoice-status":{"post":{"operationId":"refreshFattureInCloudEInvoiceStatus","description":"Fetch the canonical SDI status of one exported invoice from Fatture in Cloud and store it.","tags":["Integrations"],"summary":"Refresh an invoice's SDI status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Status refresh enqueued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Fatture in Cloud integration is not active"},"409":{"description":"Invoice is not exported"}}}},"/integrations/fatture-in-cloud/sync-log":{"get":{"operationId":"listFattureInCloudSyncLogs","description":"Retrieve Fatture in Cloud synchronization log entries for the authenticated lab, including fiscal numbers and SDI status. Supports filtering by entity type and entity ID.","tags":["Integrations"],"summary":"List Fatture in Cloud sync logs","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of sync log entries ordered by most recent first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fattureInCloudAccountId":{"type":"string"},"entityType":{"type":"string"},"entityId":{"type":["string","null"]},"invoiceId":{"type":["string","null"]},"creditNoteId":{"type":["string","null"]},"action":{"type":"string"},"phase":{"type":"string"},"status":{"type":"string"},"documentSyncStatus":{"type":"string"},"providerAttempts":{"type":"integer"},"attemptNumber":{"type":"integer"},"providerEntityId":{"type":["string","null"]},"lastError":{"type":["string","null"]},"errorMessage":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"fiscalNumber":{"type":["integer","null"]},"fiscalNumeration":{"type":["string","null"]},"fiscalYear":{"type":["integer","null"]},"eiStatus":{"type":"string"}},"required":["id","fattureInCloudAccountId","entityType","entityId","invoiceId","creditNoteId","action","phase","status","documentSyncStatus","providerAttempts","attemptNumber","providerEntityId","lastError","errorMessage","createdAt","fiscalNumber","fiscalNumeration","fiscalYear","eiStatus"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/invoices":{"get":{"operationId":"listInvoices","description":"Retrieve a paginated list of invoices for the authenticated lab. Supports filtering by status, clinic, and search text.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"draft"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","issueDate","dueDate","invoiceNumber","totalAmount"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"clinicId","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"partnershipId","in":"query"},{"schema":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"required":false,"name":"currency","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"example":false},"required":false,"name":"allTime","in":"query"}],"responses":{"200":{"description":"Paginated list of invoices with clinic details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":["string","null"]},"status":{"type":"string"},"subtotal":{"type":"string"},"taxAmount":{"type":["string","null"]},"totalAmount":{"type":"string"},"amountPaid":{"type":"string"},"amountDue":{"type":"string"},"issueDate":{"type":"string"},"dueDate":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","invoiceNumber","clinicId","clinicName","status","subtotal","taxAmount","totalAmount","amountPaid","amountDue","issueDate","dueDate","createdAt"],"additionalProperties":{}}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters"}}},"post":{"operationId":"createInvoice","description":"Create a new invoice for a clinic. Lab-only. Automatically generates an invoice number; issued invoices create the corresponding account transaction while drafts remain ledger-neutral.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"clinicId":{"type":"string","minLength":1},"orderId":{"type":"string"},"doctorUserId":{"type":"string","minLength":1},"status":{"type":"string","enum":["draft","sent"],"default":"draft"},"discountAmount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"default":"GBP"},"issueDate":{"type":"string"},"dueDate":{"type":"string"},"notes":{"type":"string","maxLength":10000},"terms":{"type":"string","maxLength":20000},"items":{"type":"array","items":{"type":"object","properties":{"orderId":{"type":"string"},"orderItemId":{"type":"string"},"description":{"type":"string","minLength":1,"maxLength":500},"quantity":{"type":"integer","exclusiveMinimum":0,"maximum":1000000,"default":1},"unitPrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["description","unitPrice"],"additionalProperties":false},"minItems":1,"maxItems":100}},"required":["clinicId","issueDate","items"],"additionalProperties":false}}}},"responses":{"201":{"description":"Invoice created successfully with line items","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"},"items":{"type":"array","items":{}}},"required":["id","invoiceNumber","items"],"additionalProperties":{}}}}},"400":{"description":"Request body failed validation"},"404":{"description":"Specified clinic not found"},"409":{"description":"Issuance requires more than the supported number of prepayments"},"422":{"description":"Idempotency-Key was already used with a different invoice body"}}}},"/invoices/generate-from-orders":{"post":{"operationId":"generateInvoicesFromOrders","description":"Batch-generate invoices from completed orders, grouped by clinic. Lab-only. Uses lab finance settings for default tax rate and due date calculation.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"orderIds":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":50},"issueDate":{"type":"string"},"dueDate":{"type":"string"},"notes":{"type":"string","maxLength":10000}},"required":["orderIds"],"additionalProperties":false}}}},"responses":{"201":{"description":"Invoices generated successfully with invoice IDs and count","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"invoiceIds":{"type":"array","items":{"type":"string"}},"count":{"type":"number"}},"required":["success","invoiceIds","count"]}}}},"400":{"description":"Request body failed validation"},"404":{"description":"None of the specified orders were found for this lab"}}}},"/invoices/by-order/{orderId}":{"get":{"operationId":"getInvoiceByOrder","description":"Look up the invoice associated with a specific order. Returns the full invoice detail including items and payments.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"The order ID to look up the invoice for","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"The order ID to look up the invoice for","name":"orderId","in":"path"}],"responses":{"200":{"description":"Full invoice detail for the specified order","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"}},"required":["id","invoiceNumber"],"additionalProperties":{}}}}},"404":{"description":"No invoice found for the specified order"}}}},"/invoices/{id}":{"get":{"operationId":"getInvoice","description":"Retrieve full invoice details including line items, payments, clinic info, and Xero sync status.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full invoice detail with items, payments, and related entities","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"}},"required":["id","invoiceNumber"],"additionalProperties":{}}}}},"404":{"description":"Invoice not found or does not belong to this lab"}}},"put":{"operationId":"updateInvoice","description":"Edit draft invoice metadata or perform an allowed lifecycle transition. Financial headers are derived from line items and payment states are ledger-derived.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["draft","sent","paid","partially_paid","overdue","cancelled"]},"doctorUserId":{"type":["string","null"],"minLength":1},"discountAmount":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"dueDate":{"type":"string"},"notes":{"type":["string","null"],"maxLength":10000},"terms":{"type":["string","null"],"maxLength":20000}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated invoice detail","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"}},"required":["id","invoiceNumber"],"additionalProperties":{}}}}},"400":{"description":"Request body failed validation"},"404":{"description":"Invoice not found or does not belong to this lab"},"409":{"description":"Invoice has an online payment in progress or issuance requires too many prepayments"}}},"delete":{"operationId":"deleteInvoice","description":"Delete a draft invoice. Only invoices in draft status can be deleted; use status update to cancel non-draft invoices instead.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invoice deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invoice is not in draft status and cannot be deleted"},"404":{"description":"Invoice not found or does not belong to this lab"}}}},"/invoices/{id}/payment":{"post":{"operationId":"recordInvoicePayment","description":"Record a manual payment against an invoice. Lab-only. Automatically updates invoice status (partially_paid/paid), creates a transaction record, and triggers Xero payment sync if configured.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":255},"required":true,"name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"method":{"type":"string","enum":["card","bank_transfer","cash","cheque","stripe","direct_debit","other"]},"reference":{"type":"string","maxLength":500},"paidAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"notes":{"type":"string","maxLength":2000},"reportingFxRate":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"reportingFxEffectiveDate":{"type":"string"}},"required":["amount","method"],"additionalProperties":false}}}},"responses":{"201":{"description":"Payment recorded with updated invoice status and remaining balance","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"paymentId":{"type":"string"},"invoiceStatus":{"type":"string"},"amountPaid":{"type":"string"},"amountDue":{"type":"string"},"overpaymentAmount":{"type":"string"}},"required":["success","paymentId","invoiceStatus","amountPaid","amountDue","overpaymentAmount"]}}}},"400":{"description":"Payment data failed validation"},"404":{"description":"Invoice not found or does not belong to this lab"},"409":{"description":"Idempotent request is still being processed or the invoice has an online payment in progress"},"422":{"description":"Idempotency-Key was reused with different payment parameters"}}}},"/invoices/{id}/email":{"post":{"operationId":"emailInvoice","description":"Send an invoice via email to the clinic's billing or primary email address. Lab-only. Also emits an in-app notification to clinic owners.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"responses":{"200":{"description":"Invoice email sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Clinic has no email address configured"},"404":{"description":"Invoice not found or does not belong to this lab"},"429":{"description":"Invoice email send budget exceeded"}}}},"/invoices/{id}/archive":{"post":{"operationId":"archiveInvoice","description":"Toggle the archive status of an invoice. If currently archived, it will be unarchived and vice versa. Archived invoices are excluded from default listing views.","tags":["Invoices"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"}],"responses":{"200":{"description":"Archive status toggled, returns new archived state","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"archived":{"type":"boolean"}},"required":["success","archived"]}}}},"404":{"description":"Invoice not found or does not belong to this lab"}}}},"/credit-notes":{"get":{"operationId":"listCreditNotes","description":"Retrieve a paginated list of credit notes for the authenticated lab. Supports filtering by status and search text.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"issued"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","issueDate","creditNoteNumber","totalAmount"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of credit notes with clinic and allocation details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createCreditNote","description":"Create an invoice-backed or manual account credit note. Invoice-backed clinic, partnership, currency, doctor identity, tax rate, and pricing mode are derived from the source invoice; manual doctor display data is resolved from a verified clinic member.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"reason":{"type":"string","maxLength":2000},"notes":{"type":"string","maxLength":10000},"invoiceId":{"type":"string","minLength":1},"items":{"type":"array","items":{"type":"object","properties":{"orderId":{"type":"string"},"orderItemId":{"type":"string"},"description":{"type":"string","minLength":1,"maxLength":500},"quantity":{"type":"integer","exclusiveMinimum":0,"maximum":1000000,"default":1},"unitPrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"invoiceItemId":{"type":"string"}},"required":["description","unitPrice"],"additionalProperties":false},"minItems":1,"maxItems":100}},"required":["invoiceId","items"],"additionalProperties":false},{"type":"object","properties":{"reason":{"type":"string","maxLength":2000},"notes":{"type":"string","maxLength":10000},"clinicId":{"type":"string","minLength":1},"doctorUserId":{"type":"string","minLength":1},"items":{"type":"array","items":{"type":"object","properties":{"orderId":{"type":"string"},"orderItemId":{"type":"string"},"description":{"type":"string","minLength":1,"maxLength":500},"quantity":{"type":"integer","exclusiveMinimum":0,"maximum":1000000,"default":1},"unitPrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["description","unitPrice"],"additionalProperties":false},"minItems":1,"maxItems":100}},"required":["clinicId","items"],"additionalProperties":false}]}}}},"responses":{"201":{"description":"Credit note created successfully with ID and number","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"creditNoteNumber":{"type":"string"}},"required":["id","creditNoteNumber"]}}}},"400":{"description":"Request body failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"422":{"description":"Idempotency-Key was already used with a different credit-note body"}}}},"/credit-notes/{id}":{"get":{"operationId":"getCreditNote","description":"Retrieve full credit note details including line items, allocations, and remaining balance.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full credit note detail with items and allocations","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"creditNoteNumber":{"type":"string"}},"required":["id","creditNoteNumber"],"additionalProperties":{}}}}},"404":{"description":"Credit note not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"voidCreditNote","description":"Void a credit note. Lab-only. Cannot void a credit note that has existing allocations; deallocate first.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"}],"responses":{"200":{"description":"Credit note voided successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Credit note has existing allocations and cannot be voided","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Credit note not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/credit-notes/{id}/email":{"post":{"operationId":"emailCreditNote","description":"Send a credit note via email to the clinic's billing or primary email address. Lab-only. Honours the lab's sales-credit email template setting.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"}],"responses":{"200":{"description":"Credit note email sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Clinic has no email address or the sales-credit template is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Credit note not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Credit note email send budget exceeded"}}}},"/credit-notes/{id}/allocate":{"post":{"operationId":"allocateCreditNote","description":"Allocate credit from a credit note to an invoice. Lab-only. Reduces the invoice's amount due and the credit note's remaining balance. May mark the invoice as paid if fully covered.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"idempotencyKey":{"type":"string","minLength":8,"maxLength":200},"invoiceId":{"type":"string","minLength":1},"amount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["idempotencyKey","invoiceId","amount"],"additionalProperties":false}}}},"responses":{"200":{"description":"Credit allocated with updated remaining credit and invoice amount due","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"newRemaining":{"type":"string"},"newAmountDue":{"type":"string"}},"required":["success","newRemaining","newAmountDue"]}}}},"400":{"description":"Amount exceeds remaining credit, credit note is voided, or validation failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Credit note or target invoice not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Credit or invoice balance changed, or the invoice has an online payment in progress","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/credit-notes/{id}/allocations/{allocationId}":{"delete":{"operationId":"deallocateCreditNote","description":"Reverse a credit-note allocation. Lab-only. Restores the credit note's remaining balance and the invoice's amount due (may revert the invoice from paid).","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Allocation ID to reverse","example":"550e8400-e29b-41d4-a716-446655440001"},"required":true,"description":"Allocation ID to reverse","name":"allocationId","in":"path"}],"responses":{"200":{"description":"Allocation reversed with updated balances","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"invoiceStatus":{"type":"string"},"newRemaining":{"type":"string"},"newAmountDue":{"type":"string"}},"required":["success","invoiceStatus","newRemaining","newAmountDue"]}}}},"400":{"description":"Allocation already reversed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Allocation, credit note, or invoice not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Balance changed concurrently; retry","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/credit-notes/{id}/refund":{"post":{"operationId":"refundCreditNote","description":"Process a cash refund for remaining credit on a credit note. Lab-only. Creates a refund record and corresponding transaction.","tags":["Credit Notes"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"idempotencyKey":{"type":"string","minLength":8,"maxLength":200},"amount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"method":{"type":"string","enum":["bank_transfer","cash","cheque","card"],"default":"bank_transfer"},"reference":{"type":"string","maxLength":500},"notes":{"type":"string","maxLength":2000},"reportingFxRate":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"reportingFxEffectiveDate":{"type":"string"}},"required":["idempotencyKey","amount"],"additionalProperties":false}}}},"responses":{"201":{"description":"Refund processed successfully with refund ID","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"refundId":{"type":"string"}},"required":["success","refundId"]}}}},"400":{"description":"Amount exceeds remaining credit or validation failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Credit note not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/statements":{"get":{"operationId":"listStatements","description":"Retrieve a paginated list of statements for the authenticated lab. Lab-only. Supports filtering by status and search text.","tags":["Statements"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"draft"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","periodStart","statementNumber","totalDue"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of statements with clinic details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Statement email delivery is already in progress","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"generateStatement","description":"Generate a new statement for a clinic with outstanding or activity data. Lab-only. Automatically calculates aged balances, line items, and running totals.","tags":["Statements"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"partnershipId":{"type":"string","minLength":1},"type":{"type":"string","enum":["outstanding","activity"],"default":"outstanding"},"asOfDate":{"type":"string"},"periodStart":{"type":"string"},"periodEnd":{"type":"string"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"notes":{"type":"string","maxLength":10000}},"required":["partnershipId","currency"],"additionalProperties":false}}}},"responses":{"201":{"description":"Statement generated successfully with statement ID and number","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"statementId":{"type":"string"},"statementNumber":{"type":"string"}},"required":["success","statementId","statementNumber"]}}}},"400":{"description":"Request body failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Statement line-item limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/statements/{id}":{"get":{"operationId":"getStatement","description":"Retrieve full statement details including line items, aged balances, and totals. Lab-only.","tags":["Statements"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Statement ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Statement ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full statement detail with line items and balance breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"statementNumber":{"type":"string"}},"required":["id","statementNumber"],"additionalProperties":{}}}}},"404":{"description":"Statement not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/statements/{id}/email":{"post":{"operationId":"emailStatement","description":"Send a statement via email to the clinic's billing or primary email address. Lab-only. Updates the statement status to sent.","tags":["Statements"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Statement ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Statement ID","name":"id","in":"path"}],"responses":{"200":{"description":"Statement email sent and status updated to sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Clinic has no email address configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Statement or clinic not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/consolidations":{"get":{"operationId":"listConsolidations","description":"Retrieve a paginated list of invoice consolidations for the authenticated lab. Lab-only. Supports filtering by status and search text.","tags":["Consolidations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"draft"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","consolidationNumber","totalAmount","dueDate"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of consolidations with invoice counts and totals","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createConsolidation","description":"Create a new invoice consolidation by grouping multiple invoices. Lab-only. Links selected invoices to the consolidation and calculates aggregate totals.","tags":["Consolidations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"invoiceIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100},"periodStart":{"type":"string"},"periodEnd":{"type":"string"},"issueDate":{"type":"string"},"dueDate":{"type":"string"},"showItemBreakdown":{"type":"boolean","default":true},"type":{"type":"string","enum":["solo_doctor"]},"doctorUserId":{"type":"string","minLength":1}},"required":["invoiceIds","type","doctorUserId"],"additionalProperties":false},{"type":"object","properties":{"invoiceIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100},"periodStart":{"type":"string"},"periodEnd":{"type":"string"},"issueDate":{"type":"string"},"dueDate":{"type":"string"},"showItemBreakdown":{"type":"boolean","default":true},"type":{"type":"string","enum":["billing_owner"]},"billingOwnerClinicId":{"type":"string","minLength":1}},"required":["invoiceIds","type","billingOwnerClinicId"],"additionalProperties":false}]}}}},"responses":{"201":{"description":"Consolidation created with ID and consolidation number","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"consolidationId":{"type":"string"},"consolidationNumber":{"type":"string"}},"required":["success","consolidationId","consolidationNumber"]}}}},"400":{"description":"One or more invoices not found or validation failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/consolidations/{id}":{"get":{"operationId":"getConsolidation","description":"Retrieve full consolidation details including linked invoices and totals. Lab-only.","tags":["Consolidations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Consolidation ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Consolidation ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full consolidation detail with linked invoices","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"consolidationNumber":{"type":"string"}},"required":["id","consolidationNumber"],"additionalProperties":{}}}}},"404":{"description":"Consolidation not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteConsolidation","description":"Delete a draft consolidation and unlink its invoices. Lab-only. Only consolidations in draft status can be deleted.","tags":["Consolidations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Consolidation ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Consolidation ID","name":"id","in":"path"}],"responses":{"200":{"description":"Consolidation deleted and invoices unlinked","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Consolidation is not in draft status and cannot be deleted","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Consolidation not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/consolidations/{id}/email":{"post":{"operationId":"emailConsolidation","description":"Send a consolidated invoice via email to the billing-owner clinic. Lab-only. Honours the lab's consolidated-invoice email template setting.","tags":["Consolidations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Consolidation ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Consolidation ID","name":"id","in":"path"}],"responses":{"200":{"description":"Consolidated invoice email sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"No billing-owner email or the consolidated-invoice template is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Consolidation not found or does not belong to this lab","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Consolidation authority or invoice eligibility changed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Consolidation email send budget exceeded"}}}},"/transactions":{"get":{"operationId":"listTransactions","description":"Retrieve a paginated list of financial transactions (invoices, payments, credit notes, refunds) for the authenticated lab. Supports filtering by type, clinic, and date range.","tags":["Transactions"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"invoice"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"clinicId","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"partnershipId","in":"query"},{"schema":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"required":false,"name":"currency","in":"query"},{"schema":{"type":"string","example":"2025-01-15"},"required":false,"name":"dateFrom","in":"query"},{"schema":{"type":"string","example":"2025-01-15"},"required":false,"name":"dateTo","in":"query"},{"schema":{"type":"string","enum":["transactionDate","createdAt","amount"],"default":"transactionDate","example":"transactionDate"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of transactions with related entity details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/overpayments":{"get":{"operationId":"listOverpayments","description":"Retrieve a paginated list of payments with overpayment amounts for the authenticated lab. Includes refund totals and remaining overpayment balances.","tags":["Overpayments"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":10000,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of overpayments with clinic names, refund totals, and remaining balances","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"invoiceId":{"type":["string","null"]},"labId":{"type":"string"},"clinicId":{"type":"string"},"amount":{"type":"string"},"overpaymentAmount":{"type":"string"},"currency":{"type":"string"},"method":{"type":"string"},"status":{"type":"string"},"reference":{"type":["string","null"]},"paidAt":{"type":["string","null"]},"createdAt":{"type":"string"},"clinicName":{"type":["string","null"]},"invoiceNumber":{"type":["string","null"]},"refundedAmount":{"type":"string"},"remainingOverpayment":{"type":"string"}},"required":["id","invoiceId","labId","clinicId","amount","overpaymentAmount","currency","method","status","reference","paidAt","createdAt","clinicName","invoiceNumber","refundedAmount","remainingOverpayment"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}}}}},"/overpayments/{id}/refund":{"post":{"operationId":"refundOverpayment","description":"Process a refund for an overpayment on a completed payment. Lab-only. Creates a refund record and corresponding account-debit transaction.","tags":["Overpayments"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Payment ID with the overpayment","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Payment ID with the overpayment","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"idempotencyKey":{"type":"string","minLength":8,"maxLength":200},"amount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"method":{"type":"string","enum":["bank_transfer","cash","cheque","card","stripe"],"default":"bank_transfer"},"reference":{"type":"string","maxLength":500},"notes":{"type":"string","maxLength":2000}},"required":["idempotencyKey","amount"],"additionalProperties":false}}}},"responses":{"201":{"description":"Overpayment refund processed successfully with refund ID","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"refundId":{"type":"string"},"status":{"type":"string","enum":["pending","completed"]}},"required":["success","refundId","status"]}}}},"202":{"description":"Stripe refund is pending provider confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"refundId":{"type":"string"},"status":{"type":"string","enum":["pending"]}},"required":["success","refundId","status"]}}}},"400":{"description":"Amount exceeds remaining overpayment or validation failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Payment not found or has no overpayment balance","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Idempotency key or refund method conflicts"},"502":{"description":"Stripe could not confirm the refund"},"503":{"description":"Stripe is not configured safely"}}}},"/finance-settings":{"get":{"operationId":"getFinanceSettings","description":"Retrieve the lab's finance settings including tax rates, payment terms, invoice automation, late fee configuration, and Stripe Connect status. Lab-only.","tags":["Finance Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Lab finance settings object, returns defaults if not yet configured","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{}}}}}},"403":{"description":"Only lab organizations can access finance settings","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"put":{"operationId":"updateFinanceSettings","description":"Update the lab's finance settings. Lab-only. Creates the settings record if it does not yet exist (upsert behavior).","tags":["Finance Settings"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"invoiceDuePolicy":{"type":"string","enum":["net_days","day_of_month"]},"invoiceTermDays":{"type":["integer","null"],"minimum":1,"maximum":365},"invoiceDueDay":{"type":["integer","null"],"minimum":1,"maximum":31},"invoiceDueMonthRule":{"type":["string","null"],"enum":["next_occurrence","following_month",null]},"invoiceReminderOnIssue":{"type":"boolean"},"invoiceReminderOffsetsDays":{"type":"array","items":{"anyOf":[{"type":"number","enum":[-3]},{"type":"number","enum":[0]},{"type":"number","enum":[7]},{"type":"number","enum":[14]}]},"maxItems":4},"creditHoldEnabledByDefault":{"type":"boolean"},"paymentTerms":{"type":["string","null"]},"bankingDetails":{"type":["string","null"]},"nominalCode":{"type":["string","null"]},"invoiceRetentionMonths":{"type":"integer","minimum":1,"maximum":120},"showPatientInStatements":{"type":"boolean"},"statementAddressLower":{"type":"boolean"},"statementDay":{"type":"integer","minimum":1,"maximum":31},"termsAndConditions":{"type":["string","null"]},"lateFeeEnabled":{"type":"boolean"},"lateFeeType":{"type":"string","enum":["flat","percentage"]},"lateFeeAmount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"lateFeeCurrency":{"type":["string","null"],"enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR",null]},"lateFeeFrequency":{"type":"string","enum":["once","monthly"]},"lateFeeGracePeriod":{"type":"integer","minimum":0},"lateFeeLabel":{"type":["string","null"]},"salesInvoiceTemplateEnabled":{"type":"boolean"},"salesInvoiceAutomate":{"type":"boolean"},"salesCreditTemplateEnabled":{"type":"boolean"},"salesCreditAutomate":{"type":"boolean"},"statementTemplateEnabled":{"type":"boolean"},"statementAutomate":{"type":"boolean"},"consolidatedInvoiceTemplateEnabled":{"type":"boolean"},"consolidatedInvoiceAutomate":{"type":"boolean"},"taxMode":{"type":"string","enum":["none","exclusive","inclusive"]},"defaultTaxRate":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"defaultPaymentPolicy":{"type":"string","enum":["pay_on_invoice","pay_in_advance"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated finance settings object","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{}}}}}},"400":{"description":"Request body failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Only lab organizations can manage finance settings","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/finances/cashflow":{"get":{"operationId":"getCashflow","description":"Return bounded monthly cash receipts, refunds, expenses, and net cash in the lab reporting currency.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["7m","12m","ytd"],"default":"7m"},"required":false,"name":"period","in":"query"}],"responses":{"200":{"description":"Monthly cashflow","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string","enum":["7m","12m","ytd"]},"currency":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"points":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string"},"receipts":{"type":"string"},"refunds":{"type":"string"},"expenses":{"type":"string"},"net":{"type":"string"}},"required":["month","receipts","refunds","expenses","net"]}},"totals":{"type":"object","properties":{"receipts":{"type":"string"},"refunds":{"type":"string"},"expenses":{"type":"string"},"net":{"type":"string"}},"required":["receipts","refunds","expenses","net"]}},"required":["period","currency","from","to","points","totals"]}}}},"409":{"description":"Organization timezone or currency is not usable for reporting","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["ORGANIZATION_CONFIGURATION_INVALID"]}},"required":["error","code"]}}}}}}},"/finances/expense-categories":{"get":{"operationId":"listExpenseCategories","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"Expense categories","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{},"updatedAt":{}},"required":["id","labId","name","isActive","createdBy"],"additionalProperties":{}}}},"required":["data"]}}}}}},"post":{"operationId":"createExpenseCategory","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Expense category created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{},"updatedAt":{}},"required":["id","labId","name","isActive","createdBy"],"additionalProperties":{}}},"required":["data"]}}}},"409":{"description":"Duplicate category or category ceiling reached"}}}},"/finances/expense-categories/{id}":{"patch":{"operationId":"updateExpenseCategory","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"isActive":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Expense category updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{},"updatedAt":{}},"required":["id","labId","name","isActive","createdBy"],"additionalProperties":{}}},"required":["data"]}}}},"404":{"description":"Expense category not found"},"409":{"description":"Duplicate expense category name"}}},"delete":{"operationId":"deleteExpenseCategory","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Unused expense category deleted"},"404":{"description":"Expense category not found"},"409":{"description":"Expense category is in use"}}}},"/finances/expenses":{"get":{"operationId":"listExpenses","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":400,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["pending","paid","void"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"categoryId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Paginated manual expenses","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"description":{"type":"string"},"originalAmount":{"type":"string"},"originalCurrency":{"type":"string"},"reportingAmount":{"type":"string"},"reportingCurrency":{"type":"string"},"status":{"type":"string"},"occurredOn":{"type":"string"}},"required":["id","labId","categoryId","description","originalAmount","originalCurrency","reportingAmount","reportingCurrency","status","occurredOn"],"additionalProperties":{}}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Expense date range is too wide"}}},"post":{"operationId":"createExpense","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key reused only when retrying the exact same command body"},"required":true,"description":"Stable key reused only when retrying the exact same command body","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":"string","minLength":1},"supplierId":{"type":["string","null"],"minLength":1},"purchaseOrderId":{"type":["string","null"],"minLength":1},"description":{"type":"string","minLength":1,"maxLength":500},"originalAmount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"originalCurrency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"occurredOn":{"type":"string"},"paidAt":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"status":{"type":"string","enum":["pending","paid"],"default":"paid"},"reference":{"type":["string","null"],"maxLength":500},"notes":{"type":["string","null"],"maxLength":2000},"reportingFxRate":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"reportingFxEffectiveDate":{"type":"string"}},"required":["categoryId","description","originalAmount","originalCurrency","occurredOn"],"additionalProperties":false}}}},"responses":{"200":{"description":"Exact idempotent replay"},"201":{"description":"Expense created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"description":{"type":"string"},"originalAmount":{"type":"string"},"originalCurrency":{"type":"string"},"reportingAmount":{"type":"string"},"reportingCurrency":{"type":"string"},"status":{"type":"string"},"occurredOn":{"type":"string"}},"required":["id","labId","categoryId","description","originalAmount","originalCurrency","reportingAmount","reportingCurrency","status","occurredOn"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid reporting FX input"},"404":{"description":"Category, supplier, or purchase order not found"},"409":{"description":"Reference or idempotency conflict"},"422":{"description":"Idempotency-Key reused for a different expense"}}}},"/finances/expenses/{id}":{"patch":{"operationId":"updateExpense","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":"string","minLength":1},"supplierId":{"type":["string","null"],"minLength":1},"purchaseOrderId":{"type":["string","null"],"minLength":1},"description":{"type":"string","minLength":1,"maxLength":500},"originalAmount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"originalCurrency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"occurredOn":{"type":"string"},"paidAt":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"status":{"type":"string","enum":["pending","paid"]},"reference":{"type":["string","null"],"maxLength":500},"notes":{"type":["string","null"],"maxLength":2000},"reportingFxRate":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"reportingFxEffectiveDate":{"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Pending expense updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"description":{"type":"string"},"originalAmount":{"type":"string"},"originalCurrency":{"type":"string"},"reportingAmount":{"type":"string"},"reportingCurrency":{"type":"string"},"status":{"type":"string"},"occurredOn":{"type":"string"}},"required":["id","labId","categoryId","description","originalAmount","originalCurrency","reportingAmount","reportingCurrency","status","occurredOn"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid lifecycle or reporting FX input"},"404":{"description":"Expense not found"},"409":{"description":"Paid or void expense cannot be edited"}}},"delete":{"operationId":"deleteExpense","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Pending expense deleted"},"404":{"description":"Expense not found"},"409":{"description":"Paid or void expense cannot be deleted"}}}},"/finances/expenses/{id}/void":{"post":{"operationId":"voidExpense","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":1000}},"required":["reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Expense voided","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":"string"},"description":{"type":"string"},"originalAmount":{"type":"string"},"originalCurrency":{"type":"string"},"reportingAmount":{"type":"string"},"reportingCurrency":{"type":"string"},"status":{"type":"string"},"occurredOn":{"type":"string"}},"required":["id","labId","categoryId","description","originalAmount","originalCurrency","reportingAmount","reportingCurrency","status","occurredOn"],"additionalProperties":{}}},"required":["data"]}}}},"404":{"description":"Expense not found"},"409":{"description":"Expense already void"}}}},"/finances/overview":{"get":{"operationId":"getFinancesOverview","description":"Retrieve a finance dashboard overview for the authenticated lab. Lab-only. Returns key metrics (outstanding, invoiced this month, payments this month, overdue), plus recent invoices and payments.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Finance overview with key metrics, recent invoices, and recent payments","content":{"application/json":{"schema":{"type":"object","properties":{"metrics":{"type":"object","properties":{"currencyTotals":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"totalOutstanding":{"type":"string"},"invoicedThisMonth":{"type":"string"},"invoicedThisMonthCount":{"type":"number"},"paymentsThisMonth":{"type":"string"},"paymentsThisMonthCount":{"type":"number"},"overdueAmount":{"type":"string"},"overdueCount":{"type":"number"}},"required":["currency","totalOutstanding","invoicedThisMonth","invoicedThisMonthCount","paymentsThisMonth","paymentsThisMonthCount","overdueAmount","overdueCount"]}}},"required":["currencyTotals"]},"recentInvoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"},"clinicName":{"type":"string"},"clinicNameMissing":{"type":"boolean"},"status":{"type":"string"},"totalAmount":{"type":"string"},"amountDue":{"type":"string"},"dueDate":{"type":["string","null"]},"issueDate":{"type":"string"},"currency":{"type":"string"}},"required":["id","invoiceNumber","clinicName","clinicNameMissing","status","totalAmount","amountDue","dueDate","issueDate","currency"]}},"recentPayments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"invoiceId":{"type":["string","null"]},"invoiceNumber":{"type":["string","null"]},"clinicName":{"type":"string"},"clinicNameMissing":{"type":"boolean"},"amount":{"type":"string"},"method":{"type":"string"},"status":{"type":"string"},"paidAt":{"type":"string"},"currency":{"type":"string"}},"required":["id","invoiceId","invoiceNumber","clinicName","clinicNameMissing","amount","method","status","paidAt","currency"]}}},"required":["metrics","recentInvoices","recentPayments"]}}}}}}},"/finances/accounts":{"get":{"operationId":"listFinanceAccounts","description":"Retrieve a paginated list of clinic accounts with financial summaries. Lab-only. Shows total invoiced, paid, outstanding, overdue, and credit balance per partner clinic.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["clinicName","outstanding","totalInvoiced","lastPaymentDate"],"default":"clinicName","example":"clinicName"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc","example":"asc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of clinic accounts with financial aggregates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"partnershipId":{"type":"string"},"partnershipStatus":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"clinicNameMissing":{"type":"boolean"},"currency":{"type":"string"},"totalInvoiced":{"type":"string"},"totalPaid":{"type":"string"},"outstanding":{"type":"string"},"overdue":{"type":"string"},"creditBalance":{"type":"string"},"lastPaymentDate":{"type":["string","null"]},"invoiceCount":{"type":"number"},"hasActivity":{"type":"boolean"}},"required":["partnershipId","partnershipStatus","clinicId","clinicName","clinicNameMissing","currency","totalInvoiced","totalPaid","outstanding","overdue","creditBalance","lastPaymentDate","invoiceCount","hasActivity"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/finances/accounts/{id}":{"get":{"operationId":"getFinanceAccount","description":"Retrieve financial summary for a specific clinic account. Lab-only. Returns total invoiced, paid, outstanding, overdue, credit balance, and last payment date.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Partnership (account epoch) ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Partnership (account epoch) ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"required":true,"name":"currency","in":"query"}],"responses":{"200":{"description":"Clinic account financial summary","content":{"application/json":{"schema":{"type":"object","properties":{"partnershipId":{"type":"string"},"partnershipStatus":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"clinicNameMissing":{"type":"boolean"},"currency":{"type":"string"},"totalInvoiced":{"type":"string"},"totalPaid":{"type":"string"},"outstanding":{"type":"string"},"overdue":{"type":"string"},"creditBalance":{"type":"string"},"lastPaymentDate":{"type":["string","null"]},"invoiceCount":{"type":"number"},"hasActivity":{"type":"boolean"}},"required":["partnershipId","partnershipStatus","clinicId","clinicName","clinicNameMissing","currency","totalInvoiced","totalPaid","outstanding","overdue","creditBalance","lastPaymentDate","invoiceCount","hasActivity"]}}}},"404":{"description":"The requested account epoch/currency was not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/finances/aged-balances":{"get":{"operationId":"getAgedBalances","description":"Retrieve aged balance report across all partner clinics. Lab-only. Breaks down outstanding amounts into current, 30-day, 60-day, and 90+ day buckets with per-clinic and grand totals.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Filter clinics by name","example":"search term"},"required":false,"description":"Filter clinics by name","name":"search","in":"query"},{"schema":{"type":"string","description":"Reference date for ageing calculations","example":"2025-12-31"},"required":false,"description":"Reference date for ageing calculations","name":"asOfDate","in":"query"}],"responses":{"200":{"description":"Aged balance rows per clinic with bucket totals and grand totals","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"partnershipId":{"type":"string"},"partnershipStatus":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"clinicNameMissing":{"type":"boolean"},"currency":{"type":"string"},"current":{"type":"string"},"days30":{"type":"string"},"days60":{"type":"string"},"days90Plus":{"type":"string"},"total":{"type":"string"}},"required":["partnershipId","partnershipStatus","clinicId","clinicName","clinicNameMissing","currency","current","days30","days60","days90Plus","total"]}},"totals":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"current":{"type":"string"},"days30":{"type":"string"},"days60":{"type":"string"},"days90Plus":{"type":"string"},"total":{"type":"string"}},"required":["currency","current","days30","days60","days90Plus","total"]}}},"required":["rows","totals"]}}}},"400":{"description":"Historical ageing is unavailable from mutable current invoice balances","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/finances/external-adjustments":{"get":{"operationId":"listStripeExternalAdjustments","description":"List unresolved Stripe dashboard refunds, disputes, and refund gaps for the authenticated lab. Lab owner/admin only.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated unresolved external Stripe adjustments","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"paymentId":{"type":"string"},"partnershipId":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"kind":{"type":"string","enum":["refund","dispute","refund_gap"]},"amount":{"type":"string"},"resolutionAmount":{"type":"string"},"currency":{"type":"string"},"providerStatus":{"type":"string"},"effectState":{"type":"string","enum":["none","debited","reinstated"]},"reviewReason":{"type":"string"},"providerReference":{"type":["string","null"]},"paymentReference":{"type":["string","null"]},"lastProviderEventAt":{"type":"string"},"createdAt":{"type":"string"},"canResolve":{"type":"boolean"},"allowedResolutionTypes":{"type":"array","items":{"type":"string","enum":["allocate","account_adjustment"]}}},"required":["id","paymentId","partnershipId","clinicId","clinicName","kind","amount","resolutionAmount","currency","providerStatus","effectState","reviewReason","providerReference","paymentReference","lastProviderEventAt","createdAt","canResolve","allowedResolutionTypes"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}}}}},"/finances/external-adjustments/{id}":{"get":{"operationId":"getStripeExternalAdjustment","description":"View an unresolved external Stripe adjustment and source-receipt invoice candidates. Lab owner/admin only.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"External Stripe adjustment review detail","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"paymentId":{"type":"string"},"partnershipId":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"kind":{"type":"string","enum":["refund","dispute","refund_gap"]},"amount":{"type":"string"},"resolutionAmount":{"type":"string"},"currency":{"type":"string"},"providerStatus":{"type":"string"},"effectState":{"type":"string","enum":["none","debited","reinstated"]},"reviewReason":{"type":"string"},"providerReference":{"type":["string","null"]},"paymentReference":{"type":["string","null"]},"lastProviderEventAt":{"type":"string"},"createdAt":{"type":"string"},"canResolve":{"type":"boolean"},"allowedResolutionTypes":{"type":"array","items":{"type":"string","enum":["allocate","account_adjustment"]}},"stripePaymentIntentId":{"type":"string"},"stripeChargeId":{"type":"string"},"invoiceCandidates":{"type":"array","items":{"type":"object","properties":{"invoiceId":{"type":"string"},"invoiceNumber":{"type":"string"},"status":{"type":"string"},"issueDate":{"type":"string"},"totalAmount":{"type":"string"},"amountPaid":{"type":"string"},"amountDue":{"type":"string"},"sourceAllocatedAmount":{"type":"string"},"currency":{"type":"string"}},"required":["invoiceId","invoiceNumber","status","issueDate","totalAmount","amountPaid","amountDue","sourceAllocatedAmount","currency"]}}},"required":["id","paymentId","partnershipId","clinicId","clinicName","kind","amount","resolutionAmount","currency","providerStatus","effectState","reviewReason","providerReference","paymentReference","lastProviderEventAt","createdAt","canResolve","allowedResolutionTypes","stripePaymentIntentId","stripeChargeId","invoiceCandidates"]}}}},"404":{"description":"Unresolved adjustment not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/finances/external-adjustments/{id}/resolve":{"post":{"operationId":"resolveStripeExternalAdjustment","description":"Resolve one external Stripe adjustment by classifying its immutable debit against source-receipt invoices or retaining it explicitly at account level. Lab owner/admin only.","tags":["Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"idempotencyKey":{"type":"string","minLength":8,"maxLength":200},"resolutionType":{"type":"string","enum":["allocate"]},"notes":{"type":"string","minLength":1,"maxLength":2000},"allocations":{"type":"array","items":{"type":"object","properties":{"invoiceId":{"type":"string","minLength":1},"amount":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["invoiceId","amount"],"additionalProperties":false},"minItems":1,"maxItems":100}},"required":["idempotencyKey","resolutionType","notes","allocations"],"additionalProperties":false},{"type":"object","properties":{"idempotencyKey":{"type":"string","minLength":8,"maxLength":200},"resolutionType":{"type":"string","enum":["account_adjustment"]},"notes":{"type":"string","minLength":1,"maxLength":2000}},"required":["idempotencyKey","resolutionType","notes"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Adjustment review resolved","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"resolutionId":{"type":"string"},"resolutionType":{"type":"string","enum":["allocate","account_adjustment"]},"resolvedAt":{"type":"string"}},"required":["success","resolutionId","resolutionType","resolvedAt"]}}}},"400":{"description":"Invalid invoice allocation"},"404":{"description":"Unresolved adjustment not found"},"409":{"description":"Review state or idempotency conflict"}}}},"/clinic-finances/invoices":{"get":{"operationId":"listClinicInvoices","description":"Retrieve a paginated list of invoices received by the authenticated clinic. Clinic-only. Supports filtering by status, search, and sorting.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"draft"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","issueDate","dueDate","invoiceNumber","totalAmount"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"clinicId","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"partnershipId","in":"query"},{"schema":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"required":false,"name":"currency","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"example":false},"required":false,"name":"allTime","in":"query"}],"responses":{"200":{"description":"Paginated list of invoices with lab details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/clinic-finances/invoices/by-order/{orderId}":{"get":{"operationId":"getClinicInvoicesByOrder","description":"Look up invoices associated with a specific order for the authenticated clinic. Clinic-only. Returns a list since an order may have multiple invoices.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Order ID to look up invoices for","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Order ID to look up invoices for","name":"orderId","in":"path"}],"responses":{"200":{"description":"List of invoices linked to the specified order","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"},"status":{"type":"string"},"totalAmount":{"type":"string"},"amountPaid":{"type":"string"},"amountDue":{"type":"string"},"currency":{"type":"string"},"dueDate":{"type":"string"},"labId":{"type":"string"},"partnershipId":{"type":"string"}},"required":["id","invoiceNumber","status","totalAmount","amountPaid","amountDue","currency","dueDate","labId","partnershipId"]}}}}}}}},"/clinic-finances/invoices/{id}":{"get":{"operationId":"getClinicInvoice","description":"Retrieve full invoice details including line items and payment history. Clinic-only. Only returns invoices belonging to the authenticated clinic.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Invoice ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Invoice ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full invoice detail with items, payments, and lab info","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"invoiceNumber":{"type":"string"},"status":{"type":"string"},"items":{"type":"array","items":{}},"payments":{"type":"array","items":{}}},"required":["id","invoiceNumber","status","items","payments"],"additionalProperties":{}}}}},"404":{"description":"Invoice not found or does not belong to this clinic","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/clinic-finances/credit-notes":{"get":{"operationId":"listClinicCreditNotes","description":"Retrieve a paginated list of credit notes received by the authenticated clinic. Clinic-only. Supports filtering by status and search text.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"issued"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","issueDate","creditNoteNumber","totalAmount"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of credit notes with lab details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/clinic-finances/credit-notes/{id}":{"get":{"operationId":"getClinicCreditNote","description":"Retrieve full credit note details including line items and allocations. Clinic-only. Only returns credit notes belonging to the authenticated clinic.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credit note ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Credit note ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full credit note detail with items and allocations","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"creditNoteNumber":{"type":"string"},"status":{"type":"string"},"items":{"type":"array","items":{}},"allocations":{"type":"array","items":{}}},"required":["id","creditNoteNumber","status","items","allocations"],"additionalProperties":{}}}}},"404":{"description":"Credit note not found or does not belong to this clinic","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/clinic-finances/statements":{"get":{"operationId":"listClinicStatements","description":"Retrieve a paginated list of statements received by the authenticated clinic. Clinic-only. Supports filtering by status and search text.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"draft"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["createdAt","periodStart","statementNumber","totalDue"],"default":"createdAt","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","example":"desc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of statements with lab details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid or malformed query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/clinic-finances/statements/{id}":{"get":{"operationId":"getClinicStatement","description":"Retrieve full statement details including line items and balance breakdown. Clinic-only. Only returns statements belonging to the authenticated clinic.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Statement ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Statement ID","name":"id","in":"path"}],"responses":{"200":{"description":"Full statement detail with line items and aged balances","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"statementNumber":{"type":"string"},"status":{"type":"string"},"lineItems":{"type":"array","items":{}}},"required":["id","statementNumber","status","lineItems"],"additionalProperties":{}}}}},"404":{"description":"Statement not found or does not belong to this clinic","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/clinic-finances/stripe-status/{labId}":{"get":{"operationId":"getClinicStripeStatus","description":"Check whether a specific lab has Stripe Connect enabled and can accept online payments. Clinic-only. Used to determine if the pay-online option should be shown.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Lab ID to check Stripe status for","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Lab ID to check Stripe status for","name":"labId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Exact partnership/account epoch authorizing this check"},"required":true,"description":"Exact partnership/account epoch authorizing this check","name":"partnershipId","in":"query"}],"responses":{"200":{"description":"Stripe enablement status for the specified lab","content":{"application/json":{"schema":{"type":"object","properties":{"stripeEnabled":{"type":"boolean"},"labStripeAccountId":{"type":["string","null"]}},"required":["stripeEnabled","labStripeAccountId"]}}}}}}},"/clinic-finances/summary":{"get":{"operationId":"getClinicFinancesSummary","description":"Aggregate outstanding-balance summary for the authenticated clinic across all payable invoices (not page-limited), broken down per lab for grouped Stripe payment. Clinic-only.","tags":["Clinic Finances"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Outstanding totals, overdue totals, and per-lab payable breakdown with Stripe status","content":{"application/json":{"schema":{"type":"object","properties":{"outstandingCount":{"type":"integer"},"overdueCount":{"type":"integer"},"currencyTotals":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"totalOutstanding":{"type":"string"},"overdueTotal":{"type":"string"},"outstandingCount":{"type":"integer"},"overdueCount":{"type":"integer"}},"required":["currency","totalOutstanding","overdueTotal","outstandingCount","overdueCount"]}},"labs":{"type":"array","items":{"type":"object","properties":{"labId":{"type":"string"},"partnershipId":{"type":"string"},"labName":{"type":"string"},"labNameMissing":{"type":"boolean"},"outstandingTotal":{"type":"string"},"invoiceCount":{"type":"integer"},"overdueTotal":{"type":"string"},"overdueCount":{"type":"integer"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"stripeEnabled":{"type":"boolean"},"labStripeAccountId":{"type":["string","null"]},"invoiceIds":{"type":"array","items":{"type":"string"}}},"required":["labId","partnershipId","labName","labNameMissing","outstandingTotal","invoiceCount","overdueTotal","overdueCount","currency","stripeEnabled","labStripeAccountId","invoiceIds"]}}},"required":["outstandingCount","overdueCount","currencyTotals","labs"]}}}}}}},"/products":{"get":{"tags":["Products"],"operationId":"listProducts","description":"Retrieve a paginated list of products for the authenticated lab, grouped by category. Supports batch fetch by IDs, filtering by category/standard/search, and sorting. Includes file requirements, materials, inventory requirements, and custom fields.","summary":"List products","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"ids","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"categoryId","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"standardId","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"includeHidden","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"onlyInactive","in":"query"},{"schema":{"type":"string","example":"1"},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"name"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","example":"asc"},"required":false,"name":"sortOrder","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Paginated list of products with categories, materials, and custom fields","content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","items":{}},"products":{"type":"array","items":{}},"data":{"type":"array","items":{}},"workTypeLayout":{"type":"string"},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}}}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission to access products"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Products"],"operationId":"createProduct","description":"Create a new product in the lab's catalog. Supports setting category, quality standard, treatment phase, pricing, materials, file requirements, inventory requirements, and custom fields in a single request.","summary":"Create a product","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sku":{"type":["string","null"],"maxLength":50},"description":{"type":["string","null"],"maxLength":2000},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string","minLength":1},"defaultPrice":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"cogs":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"pricingUnit":{"type":"string","enum":["product","tooth","arch"],"default":"product"},"turnaroundDays":{"type":"integer","exclusiveMinimum":0},"weight":{"type":["number","null"],"minimum":0},"minTeethCount":{"type":["integer","null"],"minimum":1,"maximum":32},"maxTeethCount":{"type":["integer","null"],"minimum":1,"maximum":32},"shadeEnabled":{"type":"boolean","default":true},"implantSpecRequired":{"type":"boolean","default":false},"archSelection":{"type":["string","null"],"enum":["upper","lower","both","single",null]},"bothArchesOnly":{"type":"boolean","default":false},"hideOdontogram":{"type":"boolean","default":false},"odontogramMode":{"type":"string","enum":["single_natural","single_implant","single_any","bridge_natural","bridge_implant","bridge_any","any_natural","any_implant","any_any"],"default":"any_any"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"],"default":"step_by_step"},"quantityEnabled":{"type":"boolean","default":false},"autoAddToCart":{"type":"boolean","default":false},"upfrontPayment":{"type":"boolean","default":false},"isHidden":{"type":"boolean","default":false},"useProgressBar":{"type":"boolean","default":false},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"],"default":"physical"},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0},"laboratoryCode":{"type":["string","null"],"maxLength":50},"laboratoryDescription":{"type":["string","null"],"maxLength":2000},"productMaterials":{"type":"array","items":{"type":"object","properties":{"materialId":{"type":"string","minLength":1},"isDefault":{"type":"boolean","default":false},"markupType":{"type":"string","enum":["percentage","value"],"default":"value"},"markupPercent":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"markupValue":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["materialId"]}},"fileRequirements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fileRequirementId":{"type":"string","minLength":1},"requirementMode":{"type":"string","enum":["always_required","conditionally_required","optional"],"default":"always_required"},"requirementRules":{"type":["object","null"],"properties":{"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"fallback":{"type":"string","enum":["show_optional","hide"]}},"required":["conditions","fallback"],"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["fileRequirementId"]}},"inventoryRequirements":{"type":"array","items":{"type":"object","properties":{"inventoryItemId":{"type":"string","minLength":1},"quantityPerUnit":{"type":"number","exclusiveMinimum":0},"notes":{"type":"string","maxLength":500}},"required":["inventoryItemId","quantityPerUnit"]}},"customFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string","minLength":1,"maxLength":200},"fieldType":{"type":"string","enum":["input","textarea","odontogram","checkbox","select","radio","switch","file","number","date","image_select","image_multi_select"]},"description":{"type":"string","maxLength":500},"placeholder":{"type":"string","maxLength":200},"defaultValue":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"requirementMode":{"type":"string","enum":["always_required","conditionally_required","optional"],"default":"optional"},"requirementRules":{"type":["object","null"],"properties":{"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"fallback":{"type":"string","enum":["show_optional","hide"]}},"required":["conditions","fallback"],"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"priceModifier":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"imageKey":{"type":"string","minLength":1}},"required":["value","label"],"additionalProperties":false},"maxItems":100},"visibilityRules":{"type":["object","null"],"properties":{"logic":{"type":"string","enum":["and","or"],"default":"and"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_equals","contains","not_empty","is_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"action":{"type":"string","enum":["hide","disable"]}},"required":["conditions"],"additionalProperties":false},"fieldConfig":{"anyOf":[{"type":"object","properties":{"acceptedExtensions":{"type":"array","items":{"type":"string","pattern":"^\\.\\w+$"}},"maxFiles":{"type":"integer","exclusiveMinimum":0}},"additionalProperties":false},{"type":"object","properties":{"min":{"type":"number"},"max":{"type":"number"},"step":{"type":"number","exclusiveMinimum":0}},"additionalProperties":false},{"type":"object","properties":{"minTeeth":{"type":"integer","minimum":0,"maximum":32},"maxTeeth":{"type":"integer","minimum":0,"maximum":32},"archRestriction":{"type":["string","null"],"enum":["upper","lower",null]},"requireBothArches":{"type":"boolean"}},"additionalProperties":false},{"type":"object","properties":{"priceModifier":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"additionalProperties":false},{"type":"null"}]},"isActive":{"type":"boolean","default":true}},"required":["label","fieldType"],"additionalProperties":false},"maxItems":50},"triggerRules":{"type":["array","null"],"items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"targetProductId":{"type":"string","minLength":1},"quantity":{"type":"integer","minimum":1,"default":1},"rules":{"type":"object","properties":{"logic":{"type":"string","enum":["and","or"],"default":"and"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_equals","contains","not_empty","is_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"action":{"type":"string","enum":["hide","disable"]}},"required":["conditions"],"additionalProperties":false},"enabled":{"type":"boolean","default":true}},"required":["id","targetProductId","rules"],"additionalProperties":false}},"duplicateFromProductId":{"type":"string","format":"uuid"}},"required":["name","treatmentPhaseId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Product successfully created","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt"]}},"required":["product"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can create products"},"404":{"description":"Category, standard, treatment phase, or inventory item not found"},"409":{"description":"A product with this SKU already exists or the same image copy is in progress"},"413":{"description":"Copied option images exceed the size budget"},"429":{"description":"Product image copy budget exceeded"},"500":{"description":"Internal server error"},"504":{"description":"Product option image copy timed out"}}}},"/products/recent-custom-fields":{"get":{"tags":["Products"],"operationId":"listRecentCustomFields","description":"Return up to 100 of the most recently added custom field templates across the authenticated lab's products, deduped by (label, fieldType). Used by the product editor's Quick Add picker.","summary":"List recent custom field templates","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Recent custom field templates","content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{}}},"required":["fields"]}}}},"403":{"description":"Only labs can list custom field templates"},"500":{"description":"Internal server error"}}}},"/products/options":{"get":{"tags":["Products"],"operationId":"listProductOptions","description":"Return the authenticated lab's products as `id`/`name`/`sku` picker options from a single projection, without the categories, materials, custom fields and file requirements the paginated list carries. Capped at 1000 rows; `truncated` reports when the lab holds more.","summary":"List product picker options","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"includeHidden","in":"query"},{"schema":{"type":"string","example":"crown"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"Product picker options","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]}},"required":["id","name","sku"]}},"truncated":{"type":"boolean"}},"required":["data","truncated"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"Only labs can list product options"},"500":{"description":"Internal server error"}}}},"/products/{id}":{"get":{"tags":["Products"],"operationId":"getProduct","description":"Retrieve a single product by its ID, including its category, quality standard, materials, file requirements, inventory requirements, and custom fields. Clinic users must have an active partnership with the owning lab.","summary":"Get a product","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product details with all related data","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"category":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"standard":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"}},"required":["id","name","code"]},"productMaterials":{"type":"array","items":{}},"fileRequirements":{"type":"array","items":{}},"customFields":{"type":"array","items":{}},"inventoryRequirements":{"type":"array","items":{}}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt","category","standard","productMaterials","fileRequirements","customFields","inventoryRequirements"]}},"required":["product"]}}}},"400":{"description":"Invalid request"},"403":{"description":"User does not have permission to view this product"},"404":{"description":"Product not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Products"],"operationId":"updateProduct","description":"Update an existing product's properties, materials, file requirements, inventory requirements, and custom fields. File requirements referenced by existing files are soft-deleted (archived) rather than permanently removed.","summary":"Update a product","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sku":{"type":["string","null"],"maxLength":50},"description":{"type":["string","null"],"maxLength":2000},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string","minLength":1},"defaultPrice":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"cogs":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"pricingUnit":{"type":"string","enum":["product","tooth","arch"],"default":"product"},"turnaroundDays":{"type":"integer","exclusiveMinimum":0},"weight":{"type":["number","null"],"minimum":0},"minTeethCount":{"type":["integer","null"],"minimum":1,"maximum":32},"maxTeethCount":{"type":["integer","null"],"minimum":1,"maximum":32},"shadeEnabled":{"type":"boolean","default":true},"implantSpecRequired":{"type":"boolean","default":false},"archSelection":{"type":["string","null"],"enum":["upper","lower","both","single",null]},"bothArchesOnly":{"type":"boolean","default":false},"hideOdontogram":{"type":"boolean","default":false},"odontogramMode":{"type":"string","enum":["single_natural","single_implant","single_any","bridge_natural","bridge_implant","bridge_any","any_natural","any_implant","any_any"],"default":"any_any"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"],"default":"step_by_step"},"quantityEnabled":{"type":"boolean","default":false},"autoAddToCart":{"type":"boolean","default":false},"upfrontPayment":{"type":"boolean","default":false},"isHidden":{"type":"boolean","default":false},"useProgressBar":{"type":"boolean","default":false},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"],"default":"physical"},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0},"laboratoryCode":{"type":["string","null"],"maxLength":50},"laboratoryDescription":{"type":["string","null"],"maxLength":2000},"productMaterials":{"type":"array","items":{"type":"object","properties":{"materialId":{"type":"string","minLength":1},"isDefault":{"type":"boolean","default":false},"markupType":{"type":"string","enum":["percentage","value"],"default":"value"},"markupPercent":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"markupValue":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["materialId"]}},"fileRequirements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fileRequirementId":{"type":"string","minLength":1},"requirementMode":{"type":"string","enum":["always_required","conditionally_required","optional"],"default":"always_required"},"requirementRules":{"type":["object","null"],"properties":{"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"fallback":{"type":"string","enum":["show_optional","hide"]}},"required":["conditions","fallback"],"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["fileRequirementId"]}},"inventoryRequirements":{"type":"array","items":{"type":"object","properties":{"inventoryItemId":{"type":"string","minLength":1},"quantityPerUnit":{"type":"number","exclusiveMinimum":0},"notes":{"type":"string","maxLength":500}},"required":["inventoryItemId","quantityPerUnit"]}},"customFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string","minLength":1,"maxLength":200},"fieldType":{"type":"string","enum":["input","textarea","odontogram","checkbox","select","radio","switch","file","number","date","image_select","image_multi_select"]},"description":{"type":"string","maxLength":500},"placeholder":{"type":"string","maxLength":200},"defaultValue":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"requirementMode":{"type":"string","enum":["always_required","conditionally_required","optional"],"default":"optional"},"requirementRules":{"type":["object","null"],"properties":{"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"fallback":{"type":"string","enum":["show_optional","hide"]}},"required":["conditions","fallback"],"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"priceModifier":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"imageKey":{"type":"string","minLength":1}},"required":["value","label"],"additionalProperties":false},"maxItems":100},"visibilityRules":{"type":["object","null"],"properties":{"logic":{"type":"string","enum":["and","or"],"default":"and"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_equals","contains","not_empty","is_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"action":{"type":"string","enum":["hide","disable"]}},"required":["conditions"],"additionalProperties":false},"fieldConfig":{"anyOf":[{"type":"object","properties":{"acceptedExtensions":{"type":"array","items":{"type":"string","pattern":"^\\.\\w+$"}},"maxFiles":{"type":"integer","exclusiveMinimum":0}},"additionalProperties":false},{"type":"object","properties":{"min":{"type":"number"},"max":{"type":"number"},"step":{"type":"number","exclusiveMinimum":0}},"additionalProperties":false},{"type":"object","properties":{"minTeeth":{"type":"integer","minimum":0,"maximum":32},"maxTeeth":{"type":"integer","minimum":0,"maximum":32},"archRestriction":{"type":["string","null"],"enum":["upper","lower",null]},"requireBothArches":{"type":"boolean"}},"additionalProperties":false},{"type":"object","properties":{"priceModifier":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"additionalProperties":false},{"type":"null"}]},"isActive":{"type":"boolean","default":true}},"required":["label","fieldType"],"additionalProperties":false},"maxItems":50},"triggerRules":{"type":["array","null"],"items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"targetProductId":{"type":"string","minLength":1},"quantity":{"type":"integer","minimum":1,"default":1},"rules":{"type":"object","properties":{"logic":{"type":"string","enum":["and","or"],"default":"and"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","not_equals","contains","not_empty","is_empty","has_any","includes_any","includes_all"]},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"boolean"}]}},"required":["field","operator"],"additionalProperties":false},"minItems":1},"action":{"type":"string","enum":["hide","disable"]}},"required":["conditions"],"additionalProperties":false},"enabled":{"type":"boolean","default":true}},"required":["id","targetProductId","rules"],"additionalProperties":false}},"duplicateFromProductId":{"type":"string","format":"uuid"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Product successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt"]}},"required":["product"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can update products"},"404":{"description":"Product, category, standard, phase, or inventory item not found"},"409":{"description":"A product with this SKU already exists or the same image copy is in progress"},"413":{"description":"Copied option images exceed the size budget"},"429":{"description":"Product image copy budget exceeded"},"500":{"description":"Internal server error"},"504":{"description":"Product option image copy timed out"}}},"delete":{"tags":["Products"],"operationId":"deleteProduct","description":"Soft-delete a product by setting it to inactive. The product record is preserved for historical reference in existing orders.","summary":"Delete a product","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["product","message"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can delete products"},"404":{"description":"Product not found"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Products"],"operationId":"patchProduct","description":"Partially update a product's flags such as isHidden, isActive, or sortOrder. Supports action-based toggles (toggleHidden, toggleActive) or direct field updates.","summary":"Partially update a product","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["toggleHidden","toggleActive"]},"isHidden":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"Product partially updated","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt"]}},"required":["product"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can update products"},"404":{"description":"Product not found"},"500":{"description":"Internal server error"}}}},"/products/{id}/image":{"get":{"tags":["Products"],"operationId":"getProductImage","description":"Retrieve the image associated with a product from R2 storage. Returns the raw image binary with appropriate content-type headers.","summary":"Get product image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product image binary data"}}},"post":{"tags":["Products"],"operationId":"uploadProductImage","description":"Upload or replace the image for a product. Accepts JPEG, PNG, WebP, or GIF up to 5MB via multipart form data.","summary":"Upload product image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product image successfully uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt"]}},"required":["product"]}}}},"400":{"description":"Invalid file type or file too large"},"403":{"description":"Only labs can upload product images"},"404":{"description":"Product not found"},"500":{"description":"Internal server error or storage not configured"}}},"delete":{"tags":["Products"],"operationId":"deleteProductImage","description":"Remove the image associated with a product from R2 storage and clear the imageKey reference.","summary":"Delete product image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product image successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"standardId":{"type":["string","null"]},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]},"cogs":{"type":["string","null"]},"pricingUnit":{"type":"string"},"turnaroundDays":{"type":["number","null"]},"weight":{"type":["string","null"]},"minTeethCount":{"type":["number","null"]},"maxTeethCount":{"type":["number","null"]},"shadeEnabled":{"type":"boolean"},"implantSpecRequired":{"type":"boolean"},"archSelection":{"type":["string","null"]},"bothArchesOnly":{"type":"boolean"},"hideOdontogram":{"type":"boolean"},"odontogramMode":{"type":"string"},"configuratorType":{"type":"string","enum":["single_page","step_by_step"]},"quantityEnabled":{"type":"boolean"},"autoAddToCart":{"type":"boolean"},"upfrontPayment":{"type":"boolean"},"isHidden":{"type":"boolean"},"useProgressBar":{"type":"boolean"},"fulfillmentMode":{"type":"string","enum":["physical","digital_only"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"laboratoryCode":{"type":["string","null"]},"laboratoryDescription":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"triggerRules":{},"treatmentPhaseName":{"type":["string","null"]},"treatmentPhaseSortOrder":{"type":["number","null"]},"treatmentPhaseStartPolicy":{"type":["string","null"]},"treatmentPhaseLeadTimeDays":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","categoryId","standardId","treatmentPhaseId","name","sku","description","defaultPrice","cogs","pricingUnit","turnaroundDays","weight","minTeethCount","maxTeethCount","shadeEnabled","implantSpecRequired","archSelection","bothArchesOnly","hideOdontogram","odontogramMode","configuratorType","quantityEnabled","autoAddToCart","upfrontPayment","isHidden","useProgressBar","fulfillmentMode","isActive","sortOrder","laboratoryCode","laboratoryDescription","imageKey","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["product","message"]}}}},"400":{"description":"Product has no image"},"403":{"description":"Only labs can delete product images"},"404":{"description":"Product not found"},"500":{"description":"Internal server error or storage not configured"}}}},"/products/{id}/custom-field-image":{"get":{"tags":["Products"],"operationId":"getCustomFieldImage","description":"Retrieve a custom field option image from R2 storage by its key. Validates that the image key belongs to the authenticated organization.","summary":"Get custom field image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"},{"schema":{"type":"string","example":"organizations/org-id/products/product-id/image.jpg"},"required":false,"name":"key","in":"query"}],"responses":{"200":{"description":"Custom field image binary data"}}},"post":{"tags":["Products"],"operationId":"uploadCustomFieldImage","description":"Upload an image for a product custom field option (e.g. image_select or image_multi_select). Accepts JPEG, PNG, WebP, or GIF up to 5MB via multipart form data with a fieldId.","summary":"Upload custom field image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Custom field image successfully uploaded with imageKey","content":{"application/json":{"schema":{"type":"object","properties":{"imageKey":{"type":"string"}},"required":["imageKey"]}}}},"400":{"description":"Invalid file type, file too large, or missing fieldId"},"403":{"description":"Only labs can upload custom field images"},"404":{"description":"Product not found"},"500":{"description":"Internal server error or storage not configured"}}},"delete":{"tags":["Products"],"operationId":"deleteCustomFieldImage","description":"Remove a custom field option image from R2 storage. Validates that the image key belongs to the authenticated organization.","summary":"Delete custom field image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Product ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"imageKey":{"type":"string","minLength":1}},"required":["imageKey"]}}}},"responses":{"200":{"description":"Custom field image successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Missing image key"},"403":{"description":"Only labs can delete custom field images or image does not belong to this organization"},"404":{"description":"Not found"},"500":{"description":"Internal server error or storage not configured"}}}},"/categories":{"get":{"tags":["Categories"],"operationId":"listCategories","description":"Retrieve product categories for the authenticated lab, optionally filtered by parent or active status. Returns a hierarchical tree by default or a flat list when requested.","summary":"List categories","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string"},"required":false,"name":"parentId","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"flat","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Successfully retrieved the list of product categories","content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","items":{}}},"required":["categories"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission to access categories"},"404":{"description":"Lab not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Categories"],"operationId":"createCategory","description":"Create a new product category for the authenticated lab. Categories can be nested one level deep by specifying a parentId.","summary":"Create a category","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"subtitle":{"type":"string","maxLength":120},"description":{"type":"string","maxLength":1000},"parentId":{"type":"string"},"icon":{"type":"string","maxLength":50},"imageAspectRatio":{"type":"string","enum":["square","wide"],"default":"square"},"guideTitle":{"type":"string","maxLength":120},"guideBody":{"type":"string","maxLength":2000},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Category successfully created","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid request body or nesting depth exceeded"},"403":{"description":"Only labs can create categories"},"404":{"description":"Parent category not found"},"500":{"description":"Internal server error"}}}},"/categories/reorder":{"post":{"tags":["Categories"],"operationId":"reorderCategories","description":"Bulk update the sort order and parent assignments for product categories. Validates nesting depth constraints (max one level deep).","summary":"Reorder categories","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"parentId":{"type":["string","null"]},"sortOrder":{"type":"integer","minimum":0}},"required":["id","parentId","sortOrder"]},"minItems":1}},"required":["categories"]}}}},"responses":{"200":{"description":"Categories successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid nesting or request body"},"403":{"description":"Only labs can reorder categories"},"404":{"description":"One or more categories not found"},"500":{"description":"Internal server error"}}}},"/categories/{id}":{"get":{"tags":["Categories"],"operationId":"getCategory","description":"Retrieve a single product category by its ID. Clinic users must have an active partnership with the owning lab.","summary":"Get a category","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category details","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid request"},"403":{"description":"User does not have permission to view this category"},"404":{"description":"Category not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Categories"],"operationId":"updateCategory","description":"Update an existing product category. Validates nesting constraints if parentId is changed.","summary":"Update a category","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"subtitle":{"type":"string","maxLength":120},"description":{"type":"string","maxLength":1000},"parentId":{"type":"string"},"icon":{"type":"string","maxLength":50},"imageAspectRatio":{"type":"string","enum":["square","wide"],"default":"square"},"guideTitle":{"type":"string","maxLength":120},"guideBody":{"type":"string","maxLength":2000},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Category successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid request body or self-referencing parent"},"403":{"description":"Only labs can update categories"},"404":{"description":"Category or parent category not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Categories"],"operationId":"deleteCategory","description":"Delete a product category. If the category has associated products or bundles, it is soft-deleted (deactivated) instead of permanently removed.","summary":"Delete a category","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category deleted or deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]},"action":{"type":"string"},"message":{"type":"string"}},"required":["action","message"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can delete categories"},"404":{"description":"Category not found"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Categories"],"operationId":"toggleCategory","description":"Toggle the active/inactive status of a product category.","summary":"Toggle category active status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category active status toggled","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can toggle categories"},"404":{"description":"Category not found"},"500":{"description":"Internal server error"}}}},"/categories/{id}/image":{"get":{"tags":["Categories"],"operationId":"getCategoryImage","description":"Retrieve the image associated with a product category from R2 storage. Returns the raw image binary with appropriate content-type headers.","summary":"Get category image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category image binary data"}}},"post":{"tags":["Categories"],"operationId":"uploadCategoryImage","description":"Upload or replace the image for a product category. Accepts JPEG, PNG, WebP, or GIF up to 5MB via multipart form data.","summary":"Upload category image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Image successfully uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid file type or file too large"},"403":{"description":"Only labs can upload category images"},"404":{"description":"Category not found"},"500":{"description":"Internal server error or storage not configured"}}},"delete":{"tags":["Categories"],"operationId":"deleteCategoryImage","description":"Remove the image associated with a product category from R2 storage and clear the imageKey reference.","summary":"Delete category image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category image successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["category","message"]}}}},"400":{"description":"Category has no image"},"403":{"description":"Only labs can delete category images"},"404":{"description":"Category not found"},"500":{"description":"Internal server error or storage not configured"}}}},"/categories/{id}/guide-media":{"get":{"tags":["Categories"],"operationId":"getCategoryGuideMedia","description":"Retrieve the decision-guide media (image or video) for a product category from R2 storage. Returns the raw binary with appropriate content-type headers.","summary":"Get category guide media","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Guide media binary data"}}},"post":{"tags":["Categories"],"operationId":"uploadCategoryGuideMedia","description":"Upload or replace the decision-guide media for a product category. Accepts image (JPEG/PNG/WebP/GIF up to 5MB) or video (MP4/WebM up to 20MB) via multipart form data.","summary":"Upload category guide media","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Guide media successfully uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["category"]}}}},"400":{"description":"Invalid file type or file too large"},"403":{"description":"Only labs can upload category guide media"},"404":{"description":"Category not found"},"500":{"description":"Internal server error or storage not configured"}}},"delete":{"tags":["Categories"],"operationId":"deleteCategoryGuideMedia","description":"Remove the decision-guide media for a product category from R2 storage and clear the guideMediaKey reference.","summary":"Delete category guide media","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Guide media successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"imageAspectRatio":{"type":"string","enum":["square","wide"]},"guideTitle":{"type":["string","null"]},"guideBody":{"type":["string","null"]},"guideMediaKey":{"type":["string","null"]},"guideMediaType":{"type":["string","null"],"enum":["image","video",null]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","parentId","name","subtitle","description","icon","imageKey","imageAspectRatio","guideTitle","guideBody","guideMediaKey","guideMediaType","sortOrder","isActive","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["category","message"]}}}},"400":{"description":"Category has no guide media"},"403":{"description":"Only labs can delete category guide media"},"404":{"description":"Category not found"},"500":{"description":"Internal server error or storage not configured"}}}},"/bundles":{"get":{"tags":["Bundles"],"operationId":"listBundles","description":"Retrieve a paginated list of product bundles for the authenticated lab, with optional filtering by category, search term, and visibility status.","summary":"List bundles","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"1"},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"categoryId","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"includeHidden","in":"query"},{"schema":{"type":"string","example":"name"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","example":"asc"},"required":false,"name":"sortOrder","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Paginated list of bundles with their items","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission to access bundles"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Bundles"],"operationId":"createBundle","description":"Create a new product bundle with at least 2 products. Validates that all referenced products and category belong to the lab, and checks SKU uniqueness.","summary":"Create a bundle","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sku":{"type":"string","maxLength":50},"description":{"type":"string","maxLength":2000},"categoryId":{"type":"string"},"bundlePrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"isActive":{"type":"boolean","default":true},"isHidden":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"shareTeeth":{"type":"boolean","default":false},"shareMaterial":{"type":"boolean","default":false},"shareShade":{"type":"boolean","default":false},"quantityEnabled":{"type":"boolean","default":false},"items":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","minLength":1},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["productId"]},"minItems":2}},"required":["name","bundlePrice","items"]}}}},"responses":{"201":{"description":"Bundle successfully created with its items","content":{"application/json":{"schema":{"type":"object","properties":{"bundle":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"bundlePrice":{"type":"string"},"isActive":{"type":"boolean"},"isHidden":{"type":"boolean"},"sortOrder":{"type":"number"},"shareTeeth":{"type":"boolean"},"shareMaterial":{"type":"boolean"},"shareShade":{"type":"boolean"},"quantityEnabled":{"type":"boolean"},"createdAt":{},"updatedAt":{},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"bundleId":{"type":"string"},"productId":{"type":"string"},"sortOrder":{"type":"number"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]}},"required":["id","name","sku","defaultPrice"]}},"required":["id","bundleId","productId","sortOrder","product"]}}},"required":["id","labId","categoryId","name","sku","description","bundlePrice","isActive","isHidden","sortOrder","shareTeeth","shareMaterial","shareShade","quantityEnabled","items"]}},"required":["bundle"]}}}},"400":{"description":"Invalid request body or products not found"},"403":{"description":"Only labs can create bundles"},"404":{"description":"Category not found"},"409":{"description":"A bundle with this SKU already exists"},"500":{"description":"Internal server error"}}}},"/bundles/{id}":{"get":{"tags":["Bundles"],"operationId":"getBundle","description":"Retrieve a single bundle by its ID, including its items with full product details and category information.","summary":"Get a bundle","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Bundle ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Bundle ID","name":"id","in":"path"}],"responses":{"200":{"description":"Bundle details with items and category","content":{"application/json":{"schema":{"type":"object","properties":{"bundle":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"bundlePrice":{"type":"string"},"isActive":{"type":"boolean"},"isHidden":{"type":"boolean"},"sortOrder":{"type":"number"},"shareTeeth":{"type":"boolean"},"shareMaterial":{"type":"boolean"},"shareShade":{"type":"boolean"},"quantityEnabled":{"type":"boolean"},"createdAt":{},"updatedAt":{},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"bundleId":{"type":"string"},"productId":{"type":"string"},"sortOrder":{"type":"number"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]}},"required":["id","name","sku","defaultPrice"]}},"required":["id","bundleId","productId","sortOrder","product"]}},"category":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"required":["id","labId","categoryId","name","sku","description","bundlePrice","isActive","isHidden","sortOrder","shareTeeth","shareMaterial","shareShade","quantityEnabled","items","category"]}},"required":["bundle"]}}}},"400":{"description":"Invalid request"},"403":{"description":"User does not have permission to view this bundle"},"404":{"description":"Bundle not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Bundles"],"operationId":"updateBundle","description":"Update an existing bundle's properties and optionally replace its items. Validates category, products ownership, and SKU uniqueness.","summary":"Update a bundle","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Bundle ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Bundle ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sku":{"type":"string","maxLength":50},"description":{"type":"string","maxLength":2000},"categoryId":{"type":"string"},"bundlePrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"isActive":{"type":"boolean","default":true},"isHidden":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"shareTeeth":{"type":"boolean","default":false},"shareMaterial":{"type":"boolean","default":false},"shareShade":{"type":"boolean","default":false},"quantityEnabled":{"type":"boolean","default":false},"items":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","minLength":1},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["productId"]},"minItems":2}}}}}},"responses":{"200":{"description":"Bundle successfully updated with its items","content":{"application/json":{"schema":{"type":"object","properties":{"bundle":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"categoryId":{"type":["string","null"]},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"bundlePrice":{"type":"string"},"isActive":{"type":"boolean"},"isHidden":{"type":"boolean"},"sortOrder":{"type":"number"},"shareTeeth":{"type":"boolean"},"shareMaterial":{"type":"boolean"},"shareShade":{"type":"boolean"},"quantityEnabled":{"type":"boolean"},"createdAt":{},"updatedAt":{},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"bundleId":{"type":"string"},"productId":{"type":"string"},"sortOrder":{"type":"number"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"defaultPrice":{"type":["string","null"]}},"required":["id","name","sku","defaultPrice"]}},"required":["id","bundleId","productId","sortOrder","product"]}}},"required":["id","labId","categoryId","name","sku","description","bundlePrice","isActive","isHidden","sortOrder","shareTeeth","shareMaterial","shareShade","quantityEnabled","items"]}},"required":["bundle"]}}}},"400":{"description":"Invalid request body or products not found"},"403":{"description":"Only labs can update bundles"},"404":{"description":"Bundle or category not found"},"409":{"description":"A bundle with this SKU already exists"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Bundles"],"operationId":"deleteBundle","description":"Soft-delete a bundle by setting it to inactive. The bundle record and its items are preserved for historical reference.","summary":"Delete a bundle","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Bundle ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Bundle ID","name":"id","in":"path"}],"responses":{"200":{"description":"Bundle deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can delete bundles"},"404":{"description":"Bundle not found"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Bundles"],"operationId":"patchBundle","description":"Partially update a bundle's boolean flags such as isActive, isHidden, shareTeeth, shareMaterial, shareShade, or quantityEnabled.","summary":"Partially update a bundle","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Bundle ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Bundle ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"isActive":{"type":"boolean"},"isHidden":{"type":"boolean"},"shareTeeth":{"type":"boolean"},"shareMaterial":{"type":"boolean"},"shareShade":{"type":"boolean"},"quantityEnabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Bundle partially updated","content":{"application/json":{"schema":{"type":"object","properties":{"bundle":{}}}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can modify bundles"},"404":{"description":"Bundle not found"},"500":{"description":"Internal server error"}}}},"/materials":{"get":{"tags":["Materials"],"operationId":"listMaterials","description":"Retrieve a paginated list of materials for the authenticated lab, with optional search and inactive filtering.","summary":"List materials","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"1"},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Paginated list of materials","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission to access materials"},"404":{"description":"Lab not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Materials"],"operationId":"createMaterial","description":"Create a new material in the lab's catalog. Materials carry cost information such as value per gram; markup is configured per product on the product-material association.","summary":"Create a material","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"code":{"type":"string","maxLength":50},"valuePerGram":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"description":{"type":"string","maxLength":500},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["name"]}}}},"responses":{"201":{"description":"Material successfully created","content":{"application/json":{"schema":{"type":"object","properties":{"material":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]}},"required":["material"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can create materials"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}}},"/materials/{id}":{"get":{"tags":["Materials"],"operationId":"getMaterial","description":"Retrieve a single material by its ID. Clinic users must have an active partnership with the owning lab.","summary":"Get a material","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Material ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Material ID","name":"id","in":"path"}],"responses":{"200":{"description":"Material details","content":{"application/json":{"schema":{"type":"object","properties":{"material":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]}},"required":["material"]}}}},"400":{"description":"Invalid request"},"403":{"description":"User does not have permission to view this material"},"404":{"description":"Material not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Materials"],"operationId":"updateMaterial","description":"Update an existing material's properties including name, code, pricing, and active status.","summary":"Update a material","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Material ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Material ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"code":{"type":"string","maxLength":50},"valuePerGram":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"description":{"type":"string","maxLength":500},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0}}}}}},"responses":{"200":{"description":"Material successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"material":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]}},"required":["material"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can update materials"},"404":{"description":"Material not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Materials"],"operationId":"deleteMaterial","description":"Soft-delete a material by setting it to inactive. The material record is preserved for historical reference.","summary":"Delete a material","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Material ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Material ID","name":"id","in":"path"}],"responses":{"200":{"description":"Material deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"material":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["material","message"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can delete materials"},"404":{"description":"Material not found"},"500":{"description":"Internal server error"}}}},"/materials/{id}/icon":{"get":{"tags":["Materials"],"operationId":"getMaterialIcon","description":"Retrieve the icon image associated with a material from R2 storage. Returns the raw image binary with appropriate content-type headers.","summary":"Get material icon","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Material ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Material ID","name":"id","in":"path"}],"responses":{"200":{"description":"Material icon binary data"},"404":{"description":"Material or icon not found"},"500":{"description":"Storage not configured"}}},"post":{"tags":["Materials"],"operationId":"uploadMaterialIcon","description":"Upload or replace the icon for a material. Accepts JPEG, PNG, WebP, or GIF up to 5MB via multipart form data.","summary":"Upload material icon","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Material ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Material ID","name":"id","in":"path"}],"responses":{"200":{"description":"Icon successfully uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"material":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]}},"required":["material"]}}}},"400":{"description":"Invalid file type or file too large"},"403":{"description":"Only labs can upload material icons"},"404":{"description":"Material not found"},"500":{"description":"Internal server error or storage not configured"}}},"delete":{"tags":["Materials"],"operationId":"deleteMaterialIcon","description":"Remove the icon associated with a material from R2 storage and clear the iconKey reference.","summary":"Delete material icon","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Material ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Material ID","name":"id","in":"path"}],"responses":{"200":{"description":"Material icon successfully removed","content":{"application/json":{"schema":{"type":"object","properties":{"material":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"valuePerGram":{"type":["string","null"]},"description":{"type":["string","null"]},"iconKey":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","valuePerGram","description","iconKey","isActive","sortOrder","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["material","message"]}}}},"400":{"description":"Material has no icon"},"403":{"description":"Only labs can delete material icons"},"404":{"description":"Material not found"},"500":{"description":"Internal server error or storage not configured"}}}},"/file-requirements":{"get":{"tags":["File Requirements"],"operationId":"listFileRequirements","description":"List the lab's file requirement library entries with usage counts.","summary":"List file requirements","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"includeArchived","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string"},"required":false,"name":"page","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of file requirements","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"acceptedExtensions":{"type":"array","items":{"type":"string"}},"sortOrder":{"type":"number"},"archivedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"productsUsingCount":{"type":"number"}},"required":["id","organizationId","name","description","acceptedExtensions","sortOrder","archivedAt","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"403":{"description":"Forbidden"},"500":{"description":"Internal server error"}}},"post":{"tags":["File Requirements"],"operationId":"createFileRequirement","description":"Create a new file requirement library entry.","summary":"Create file requirement","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500},"acceptedExtensions":{"type":"array","items":{"type":"string","pattern":"^\\.\\w+$"},"minItems":1},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["name","acceptedExtensions"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"fileRequirement":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"acceptedExtensions":{"type":"array","items":{"type":"string"}},"sortOrder":{"type":"number"},"archivedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"productsUsingCount":{"type":"number"}},"required":["id","organizationId","name","description","acceptedExtensions","sortOrder","archivedAt","createdAt","updatedAt"]}},"required":["fileRequirement"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Forbidden"}}}},"/file-requirements/{id}":{"get":{"tags":["File Requirements"],"operationId":"getFileRequirement","description":"Get a single file requirement library entry.","summary":"Get file requirement","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"fileRequirement":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"acceptedExtensions":{"type":"array","items":{"type":"string"}},"sortOrder":{"type":"number"},"archivedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"productsUsingCount":{"type":"number"}},"required":["id","organizationId","name","description","acceptedExtensions","sortOrder","archivedAt","createdAt","updatedAt"]}},"required":["fileRequirement"]}}}},"404":{"description":"Not found"}}},"put":{"tags":["File Requirements"],"operationId":"updateFileRequirement","description":"Update a file requirement library entry.","summary":"Update file requirement","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500},"acceptedExtensions":{"type":"array","items":{"type":"string","pattern":"^\\.\\w+$"},"minItems":1},"sortOrder":{"type":"integer","minimum":0,"default":0}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"fileRequirement":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"acceptedExtensions":{"type":"array","items":{"type":"string"}},"sortOrder":{"type":"number"},"archivedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"productsUsingCount":{"type":"number"}},"required":["id","organizationId","name","description","acceptedExtensions","sortOrder","archivedAt","createdAt","updatedAt"]}},"required":["fileRequirement"]}}}},"404":{"description":"Not found"}}},"delete":{"tags":["File Requirements"],"operationId":"archiveFileRequirement","description":"Archive a file requirement library entry. Rejects if any products currently reference it.","summary":"Archive file requirement","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Archived","content":{"application/json":{"schema":{"type":"object","properties":{"fileRequirement":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"acceptedExtensions":{"type":"array","items":{"type":"string"}},"sortOrder":{"type":"number"},"archivedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"productsUsingCount":{"type":"number"}},"required":["id","organizationId","name","description","acceptedExtensions","sortOrder","archivedAt","createdAt","updatedAt"]}},"required":["fileRequirement"]}}}},"404":{"description":"Not found"},"409":{"description":"In use by one or more products"}}}},"/shade-systems":{"get":{"tags":["Shade Systems"],"operationId":"listShadeSystems","description":"Retrieve shade systems and their stable values for the requested lab.","summary":"List shade systems","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"defaultOnly","in":"query"},{"schema":{"type":"string"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"List of shade systems (or the default system)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}}},"required":["data"]},{"type":"object","properties":{"system":{"type":["object","null"],"properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}},"required":["system"]}]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission"},"404":{"description":"Lab not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Shade Systems"],"operationId":"createShadeSystem","description":"Create a shade system and its stable value rows.","summary":"Create a shade system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"isDefault":{"type":"boolean","default":false},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"code":{"type":"string","minLength":1,"maxLength":20},"label":{"type":"string","minLength":1,"maxLength":100},"sortOrder":{"type":"integer","minimum":0},"isActive":{"type":"boolean"}},"required":["code"],"additionalProperties":false},"default":[]}},"required":["name"]}}}},"responses":{"201":{"description":"Shade system created","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can create shade systems"},"500":{"description":"Internal server error"}}}},"/shade-systems/{id}":{"get":{"tags":["Shade Systems"],"operationId":"getShadeSystem","description":"Retrieve one shade system. Clinic users require a partnership with its lab.","summary":"Get a shade system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Shade system","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}},"required":["system"]}}}},"403":{"description":"Forbidden"},"404":{"description":"Shade system not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Shade Systems"],"operationId":"updateShadeSystem","description":"Update a shade system and reconcile stable values by ID. Renames preserve image assets.","summary":"Update a shade system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"code":{"type":"string","minLength":1,"maxLength":20},"label":{"type":"string","minLength":1,"maxLength":100},"sortOrder":{"type":"integer","minimum":0},"isActive":{"type":"boolean"}},"required":["code"],"additionalProperties":false}}}}}}},"responses":{"200":{"description":"Shade system updated","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"Invalid request body or value identity"},"403":{"description":"Only labs can update shade systems"},"404":{"description":"Shade system not found"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Shade Systems"],"operationId":"patchShadeSystem","description":"Perform a shade-system action.","summary":"Patch a shade system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string"}}}}}},"responses":{"200":{"description":"Action completed","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"Invalid action"},"403":{"description":"Only labs can update shade systems"},"404":{"description":"Shade system not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Shade Systems"],"operationId":"deleteShadeSystem","description":"Soft-delete a non-default shade system.","summary":"Delete a shade system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Shade system deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["system","message"]}}}},"400":{"description":"Cannot delete the default shade system"},"403":{"description":"Only labs can delete shade systems"},"404":{"description":"Shade system not found"},"500":{"description":"Internal server error"}}}},"/shade-systems/{id}/values/{valueId}/image":{"post":{"tags":["Shade Systems"],"operationId":"uploadShadeValueImage","description":"Upload or replace one position image for a stable shade value (maximum 5MB).","summary":"Upload shade value image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"valueId","in":"path"},{"schema":{"type":"string","enum":["occlusal","middle","gingival"]},"required":true,"name":"position","in":"query"}],"responses":{"200":{"description":"Image uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]},"url":{"type":["string","null"]}},"required":["system","url"]}}}},"400":{"description":"Invalid file or position"},"403":{"description":"Only labs can upload shade value images"},"404":{"description":"Shade system or value not found"},"500":{"description":"Internal server error"}}},"get":{"tags":["Shade Systems"],"operationId":"getShadeValueImage","description":"Stream a stable shade value's position image.","summary":"Get shade value image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"valueId","in":"path"},{"schema":{"type":"string","enum":["occlusal","middle","gingival"]},"required":true,"name":"position","in":"query"}],"responses":{"200":{"description":"Image binary data"},"404":{"description":"Image not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Shade Systems"],"operationId":"deleteShadeValueImage","description":"Clear and enqueue deletion of one shade-value image.","summary":"Delete shade value image","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"valueId","in":"path"},{"schema":{"type":"string","enum":["occlusal","middle","gingival"]},"required":true,"name":"position","in":"query"}],"responses":{"200":{"description":"Image removed","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"values":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"label":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"images":{"type":"object","properties":{"occlusal":{"type":["string","null"]},"middle":{"type":["string","null"]},"gingival":{"type":["string","null"]}},"required":["occlusal","middle","gingival"]}},"required":["id","code","label","sortOrder","isActive","images"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","isDefault","isActive","sortOrder","values","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"No image for this value/position"},"403":{"description":"Only labs can delete shade value images"},"404":{"description":"Shade value not found"},"500":{"description":"Internal server error"}}}},"/implant-systems":{"get":{"tags":["Implant Systems"],"operationId":"listImplantSystems","description":"Retrieve all persisted implant systems for the authenticated lab, including their inline platforms list. Organization defaults are initialized during creation or by the explicit settings-defaults command.","summary":"List implant systems","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"false"},"required":false,"name":"defaultOnly","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"List of implant systems (or the default, when defaultOnly)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]}}},"required":["data"]},{"type":"object","properties":{"system":{"type":["object","null"],"properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]}},"required":["system"]}]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission"},"404":{"description":"Lab not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Implant Systems"],"operationId":"createImplantSystem","description":"Create a new implant system with an inline list of platforms (connections + diameters).","summary":"Create an implant system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"manufacturer":{"type":"string","maxLength":100},"description":{"type":"string","maxLength":500},"isDefault":{"type":"boolean","default":false},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":50},"label":{"type":"string","minLength":1,"maxLength":100},"diameter":{"type":"string","maxLength":20},"isActive":{"type":"boolean","default":true}},"required":["code","label"],"additionalProperties":false},"default":[]}},"required":["name"]}}}},"responses":{"201":{"description":"Implant system created","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can create implant systems"},"500":{"description":"Internal server error"}}}},"/implant-systems/{id}":{"get":{"tags":["Implant Systems"],"operationId":"getImplantSystem","description":"Retrieve a single implant system by its ID. Clinic users must have an active partnership with the owning lab.","summary":"Get an implant system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Implant system ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Implant system ID","name":"id","in":"path"}],"responses":{"200":{"description":"Implant system","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]}},"required":["system"]}}}},"403":{"description":"Forbidden"},"404":{"description":"Implant system not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Implant Systems"],"operationId":"updateImplantSystem","description":"Update an existing implant system's properties, including the full inline platforms array.","summary":"Update an implant system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"manufacturer":{"type":"string","maxLength":100},"description":{"type":"string","maxLength":500},"isDefault":{"type":"boolean","default":false},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":50},"label":{"type":"string","minLength":1,"maxLength":100},"diameter":{"type":"string","maxLength":20},"isActive":{"type":"boolean","default":true}},"required":["code","label"],"additionalProperties":false},"default":[]}}}}}},"responses":{"200":{"description":"Implant system updated","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can update implant systems"},"404":{"description":"Implant system not found"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Implant Systems"],"operationId":"patchImplantSystem","description":"Perform an action on an implant system (e.g. setDefault).","summary":"Patch an implant system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string"}}}}}},"responses":{"200":{"description":"Action completed","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]}},"required":["system"]}}}},"400":{"description":"Invalid action"},"403":{"description":"Only labs can update implant systems"},"404":{"description":"Implant system not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Implant Systems"],"operationId":"deleteImplantSystem","description":"Soft-delete an implant system by setting it inactive.","summary":"Delete an implant system","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Implant system deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"system":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"manufacturer":{"type":["string","null"]},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"platforms":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"diameter":{"type":"string"},"isActive":{"type":"boolean"}},"required":["code","label","isActive"]}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","manufacturer","description","isDefault","isActive","sortOrder","platforms","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["system","message"]}}}},"400":{"description":"Cannot delete the default implant system"},"403":{"description":"Only labs can delete implant systems"},"404":{"description":"Implant system not found"},"500":{"description":"Internal server error"}}}},"/components":{"get":{"tags":["Components"],"operationId":"listComponents","description":"Retrieve all lab components (e.g. implant components) ordered by sort position, optionally including inactive ones.","summary":"List components","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of lab components","content":{"application/json":{"schema":{"type":"object","properties":{"components":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","isActive","sortOrder","createdAt","updatedAt"]}},"truncated":{"type":"boolean","description":"True when the lab holds more components than one response returns. The list is capped, not refused."}},"required":["components","truncated"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"Only labs can manage components"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Components"],"operationId":"createComponent","description":"Create a new lab component. If sortOrder is not specified, it is automatically set to the next available position.","summary":"Create a component","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sortOrder":{"type":"integer","minimum":0}},"required":["name"]}}}},"responses":{"201":{"description":"Component successfully created","content":{"application/json":{"schema":{"type":"object","properties":{"component":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","isActive","sortOrder","createdAt","updatedAt"]}},"required":["component"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can manage components"},"404":{"description":"Lab not found"},"409":{"description":"Component row limit reached"},"500":{"description":"Internal server error"}}}},"/components/reorder":{"post":{"tags":["Components"],"operationId":"reorderComponents","description":"Bulk reorder lab components by providing an ordered array of component IDs. Sort positions are assigned sequentially starting from 0.","summary":"Reorder components","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"componentIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["componentIds"]}}}},"responses":{"200":{"description":"Components successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can manage components"},"404":{"description":"One or more components not found"},"500":{"description":"Internal server error"}}}},"/components/settings":{"get":{"tags":["Components"],"operationId":"getComponentSettings","description":"Retrieve the lab's component display settings, such as whether components are shown in the order page.","summary":"Get component settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Component display settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"showInOrderPage":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","showInOrderPage","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can manage components"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Components"],"operationId":"updateComponentSettings","description":"Update the lab's component display settings. Creates the settings record if it does not yet exist.","summary":"Update component settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"showInOrderPage":{"type":"boolean"}},"required":["showInOrderPage"]}}}},"responses":{"200":{"description":"Component settings successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":["string","null"]},"labId":{"type":"string"},"showInOrderPage":{"type":"boolean"},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","labId","showInOrderPage","createdAt","updatedAt"]}},"required":["settings"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can manage components"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}}},"/components/{id}":{"put":{"tags":["Components"],"operationId":"updateComponent","description":"Update an existing lab component's name or active status.","summary":"Update a component","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Component ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Component ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Component successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"component":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","isActive","sortOrder","createdAt","updatedAt"]}},"required":["component"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can manage components"},"404":{"description":"Component not found"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Components"],"operationId":"deleteComponent","description":"Soft-delete a lab component by setting it to inactive. The component record is preserved for historical reference.","summary":"Delete a component","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Component ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Component ID","name":"id","in":"path"}],"responses":{"200":{"description":"Component deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"component":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","isActive","sortOrder","createdAt","updatedAt"]}},"required":["component"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can manage components"},"404":{"description":"Component not found"},"500":{"description":"Internal server error"}}}},"/treatment-phases":{"get":{"tags":["Treatment Phases"],"operationId":"listTreatmentPhases","description":"Retrieve all treatment phases for the authenticated lab, including their ordered task list and task counts.","summary":"List treatment phases","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of treatment phases with their tasks","content":{"application/json":{"schema":{"type":"object","properties":{"phases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"defaultOffsetDays":{"type":"number"},"isActive":{"type":"boolean"},"color":{"type":["string","null"]},"startPolicy":{"type":"string"},"leadTimeDays":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"taskCount":{"type":"number"},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"clinicVisible":{"type":"boolean"},"clinicTitle":{"type":["string","null"]},"task":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":["string","null"]},"icon":{"type":["string","null"]},"completionTrigger":{"type":"string"}},"required":["id","name","description","color","icon","completionTrigger"]}},"required":["id","taskId","sortOrder","isActive","clinicVisible","clinicTitle","task"]}}},"required":["id","labId","name","description","sortOrder","defaultOffsetDays","isActive","startPolicy","leadTimeDays","createdAt","updatedAt"]}}},"required":["phases"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can access treatment phases"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Treatment Phases"],"operationId":"createTreatmentPhase","description":"Create a new treatment phase with an optional ordered task list. The phase sort order is set to the next available position.","summary":"Create a treatment phase","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":["string","null"]},"defaultOffsetDays":{"type":"integer","minimum":0},"color":{"type":["string","null"]},"startPolicy":{"type":"string","enum":["ask","always","never"]},"leadTimeDays":{"type":"integer","minimum":0},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string","minLength":1},"clinicVisible":{"type":"boolean"},"clinicTitle":{"type":["string","null"],"maxLength":120},"isActive":{"type":"boolean"}},"required":["taskId"]},"maxItems":100}},"required":["name"]}}}},"responses":{"201":{"description":"Treatment phase successfully created with its tasks","content":{"application/json":{"schema":{"type":"object","properties":{"phase":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"defaultOffsetDays":{"type":"number"},"isActive":{"type":"boolean"},"color":{"type":["string","null"]},"startPolicy":{"type":"string"},"leadTimeDays":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"taskCount":{"type":"number"},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"clinicVisible":{"type":"boolean"},"clinicTitle":{"type":["string","null"]},"task":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":["string","null"]},"icon":{"type":["string","null"]},"completionTrigger":{"type":"string"}},"required":["id","name","description","color","icon","completionTrigger"]}},"required":["id","taskId","sortOrder","isActive","clinicVisible","clinicTitle","task"]}}},"required":["id","labId","name","description","sortOrder","defaultOffsetDays","isActive","startPolicy","leadTimeDays","createdAt","updatedAt"]}},"required":["phase"]}}}},"400":{"description":"Invalid request body or task references unknown task"},"403":{"description":"Only labs can create treatment phases"},"404":{"description":"Not found"},"500":{"description":"Internal server error"}}}},"/treatment-phases/reorder":{"post":{"tags":["Treatment Phases"],"operationId":"reorderTreatmentPhases","description":"Bulk reorder treatment phases by providing an ordered array of phase IDs. Sort positions are assigned sequentially starting from 0.","summary":"Reorder treatment phases","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phaseIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100}},"required":["phaseIds"]}}}},"responses":{"200":{"description":"Treatment phases successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid phaseIds array"},"403":{"description":"Only labs can reorder treatment phases"},"404":{"description":"One or more phases not found"},"500":{"description":"Internal server error"}}}},"/treatment-phases/{id}":{"get":{"tags":["Treatment Phases"],"operationId":"getTreatmentPhase","description":"Retrieve a single treatment phase by its ID, including its ordered task list.","summary":"Get a treatment phase","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Treatment phase ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Treatment phase ID","name":"id","in":"path"}],"responses":{"200":{"description":"Treatment phase details with tasks","content":{"application/json":{"schema":{"type":"object","properties":{"phase":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"defaultOffsetDays":{"type":"number"},"isActive":{"type":"boolean"},"color":{"type":["string","null"]},"startPolicy":{"type":"string"},"leadTimeDays":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"taskCount":{"type":"number"},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"clinicVisible":{"type":"boolean"},"clinicTitle":{"type":["string","null"]},"task":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":["string","null"]},"icon":{"type":["string","null"]},"completionTrigger":{"type":"string"}},"required":["id","name","description","color","icon","completionTrigger"]}},"required":["id","taskId","sortOrder","isActive","clinicVisible","clinicTitle","task"]}}},"required":["id","labId","name","description","sortOrder","defaultOffsetDays","isActive","startPolicy","leadTimeDays","createdAt","updatedAt"]}},"required":["phase"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can access treatment phases"},"404":{"description":"Treatment phase not found"},"500":{"description":"Internal server error"}}},"put":{"tags":["Treatment Phases"],"operationId":"updateTreatmentPhase","description":"Update an existing treatment phase. If `tasks` is provided, the template task list is replaced; existing orders retain their immutable task snapshots.","summary":"Update a treatment phase","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Treatment phase ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Treatment phase ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":["string","null"]},"defaultOffsetDays":{"type":"integer","minimum":0},"isActive":{"type":"boolean"},"color":{"type":["string","null"]},"startPolicy":{"type":"string","enum":["ask","always","never"]},"leadTimeDays":{"type":"integer","minimum":0},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string","minLength":1},"clinicVisible":{"type":"boolean"},"clinicTitle":{"type":["string","null"],"maxLength":120},"isActive":{"type":"boolean"}},"required":["taskId"]},"maxItems":100}}}}}},"responses":{"200":{"description":"Treatment phase successfully updated with tasks","content":{"application/json":{"schema":{"type":"object","properties":{"phase":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"defaultOffsetDays":{"type":"number"},"isActive":{"type":"boolean"},"color":{"type":["string","null"]},"startPolicy":{"type":"string"},"leadTimeDays":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"taskCount":{"type":"number"},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"taskId":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"clinicVisible":{"type":"boolean"},"clinicTitle":{"type":["string","null"]},"task":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":["string","null"]},"icon":{"type":["string","null"]},"completionTrigger":{"type":"string"}},"required":["id","name","description","color","icon","completionTrigger"]}},"required":["id","taskId","sortOrder","isActive","clinicVisible","clinicTitle","task"]}}},"required":["id","labId","name","description","sortOrder","defaultOffsetDays","isActive","startPolicy","leadTimeDays","createdAt","updatedAt"]}},"required":["phase"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can update treatment phases"},"404":{"description":"Treatment phase not found"},"409":{"description":"The requested lifecycle conflicts with task readiness, default status, or product references"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Treatment Phases"],"operationId":"deleteTreatmentPhase","description":"Permanently delete a treatment phase and its task list. Fails if the phase is referenced by any products or order phases.","summary":"Delete a treatment phase","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Treatment phase ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Treatment phase ID","name":"id","in":"path"}],"responses":{"200":{"description":"Treatment phase successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request"},"403":{"description":"Only labs can delete treatment phases"},"404":{"description":"Treatment phase not found"},"409":{"description":"Phase is referenced by products or order phases"},"500":{"description":"Internal server error"}}}},"/treatment-phases/{id}/tasks/reorder":{"post":{"tags":["Treatment Phases"],"operationId":"reorderPhaseTasks","description":"Bulk reorder the task list within a treatment phase by providing an ordered array of phase-task row IDs.","summary":"Reorder tasks within a treatment phase","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Treatment phase ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Treatment phase ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phaseTaskIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100}},"required":["phaseTaskIds"]}}}},"responses":{"200":{"description":"Phase tasks successfully reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid phaseTaskIds array"},"403":{"description":"Only labs can reorder phase tasks"},"404":{"description":"Treatment phase or rows not found"},"500":{"description":"Internal server error"}}}},"/standards":{"get":{"tags":["Standards"],"operationId":"listStandards","description":"Retrieve all quality standards for the authenticated lab, optionally including inactive ones.","summary":"List quality standards","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"false"},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"List of quality standards","content":{"application/json":{"schema":{"type":"object","properties":{"standards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","description","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["standards"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"User does not have permission to access standards"},"404":{"description":"Lab not found"},"500":{"description":"Internal server error"}}},"post":{"tags":["Standards"],"operationId":"createStandard","description":"Create a new quality standard with a unique code (bronze, silver, gold, platinum, na) for the authenticated lab.","summary":"Create a quality standard","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"code":{"type":"string","enum":["bronze","silver","gold","platinum","na"]},"description":{"type":"string","maxLength":500},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name","code"]}}}},"responses":{"201":{"description":"Quality standard successfully created","content":{"application/json":{"schema":{"type":"object","properties":{"standard":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["standard"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can create quality standards"},"409":{"description":"A standard with this code already exists"},"500":{"description":"Internal server error"}}},"put":{"tags":["Standards"],"operationId":"updateStandard","description":"Update an existing quality standard identified by the id query parameter. Validates code uniqueness if the code is being changed.","summary":"Update a quality standard","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"query"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"code":{"type":"string","enum":["bronze","silver","gold","platinum","na"]},"description":{"type":"string","maxLength":500},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Quality standard successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"standard":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["standard"]}}}},"400":{"description":"Invalid request body or missing standard ID"},"403":{"description":"Only labs can update quality standards"},"404":{"description":"Standard not found"},"409":{"description":"A standard with this code already exists"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Standards"],"operationId":"toggleStandard","description":"Toggle the active/inactive status of a quality standard identified by the id query parameter.","summary":"Toggle quality standard active status","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"query"}],"responses":{"200":{"description":"Quality standard active status toggled","content":{"application/json":{"schema":{"type":"object","properties":{"standard":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"description":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","code","description","sortOrder","isActive","createdAt","updatedAt"]}},"required":["standard"]}}}},"400":{"description":"Missing standard ID"},"403":{"description":"Only labs can toggle quality standards"},"404":{"description":"Standard not found"},"500":{"description":"Internal server error"}}}},"/price-lists":{"get":{"tags":["Price Lists"],"operationId":"listPriceLists","summary":"List stable price books and their versions","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Price books and immutable publication history","content":{"application/json":{"schema":{"type":"object","properties":{"priceLists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"},"archivedAt":{"type":["string","null"],"format":"date-time"},"archivedBy":{"type":["string","null"]},"versions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]}}},"required":["id","labId","name","description","isDefault","createdBy","createdAt","updatedAt","archivedAt","archivedBy","versions"]}}},"required":["priceLists"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}},"post":{"tags":["Price Lists"],"operationId":"createPriceList","summary":"Create a stable price book","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":["string","null"],"maxLength":2000},"isDefault":{"type":"boolean","default":false}},"required":["name"]}}}},"responses":{"201":{"description":"Price book created","content":{"application/json":{"schema":{"type":"object","properties":{"priceList":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"},"archivedAt":{"type":["string","null"],"format":"date-time"},"archivedBy":{"type":["string","null"]}},"required":["id","labId","name","description","isDefault","createdBy","createdAt","updatedAt","archivedAt","archivedBy"]}},"required":["priceList"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}":{"patch":{"tags":["Price Lists"],"operationId":"updatePriceList","summary":"Update stable price-book metadata","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":["string","null"],"maxLength":2000},"isDefault":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Price book updated","content":{"application/json":{"schema":{"type":"object","properties":{"priceList":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"},"archivedAt":{"type":["string","null"],"format":"date-time"},"archivedBy":{"type":["string","null"]}},"required":["id","labId","name","description","isDefault","createdBy","createdAt","updatedAt","archivedAt","archivedBy"]}},"required":["priceList"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/archive":{"post":{"tags":["Price Lists"],"operationId":"archivePriceList","summary":"Archive a stable price book","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"archived":{"type":"boolean","enum":[true]}},"required":["archived"]}}}},"responses":{"200":{"description":"Price book archived","content":{"application/json":{"schema":{"type":"object","properties":{"priceList":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"createdBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"},"archivedAt":{"type":["string","null"],"format":"date-time"},"archivedBy":{"type":["string","null"]}},"required":["id","labId","name","description","isDefault","createdBy","createdAt","updatedAt","archivedAt","archivedBy"]}},"required":["priceList"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions":{"post":{"tags":["Price Lists"],"operationId":"createPriceListVersion","summary":"Create the one editable draft for a price book","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cloneFromVersionId":{"type":"string","minLength":1}}}}}},"responses":{"201":{"description":"Draft created","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]}},"required":["version"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions/{versionId}":{"get":{"tags":["Price Lists"],"operationId":"getPriceListVersion","summary":"Get a version with its product and bundle rules","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"}],"responses":{"200":{"description":"Version detail","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListVersionId":{"type":"string"},"targetKind":{"type":"string","enum":["product","bundle"]},"productId":{"type":["string","null"]},"bundleId":{"type":["string","null"]},"fixedPrice":{"type":["string","null"]},"discountPercent":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListVersionId","targetKind","productId","bundleId","fixedPrice","discountPercent","createdAt","updatedAt"]}},"productRules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListVersionId":{"type":"string"},"productId":{"type":"string"},"fixedPrice":{"type":["string","null"]},"discountPercent":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListVersionId","productId","fixedPrice","discountPercent","createdAt","updatedAt"]}},"bundleRules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListVersionId":{"type":"string"},"bundleId":{"type":"string"},"fixedPrice":{"type":["string","null"]},"discountPercent":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListVersionId","bundleId","fixedPrice","discountPercent","createdAt","updatedAt"]}}},"required":["version","rules","productRules","bundleRules"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}},"patch":{"tags":["Price Lists"],"operationId":"updatePriceListVersion","summary":"Update draft defaults with optimistic concurrency","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0},"defaultDiscountPercent":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["expectedRevision","defaultDiscountPercent"]}}}},"responses":{"200":{"description":"Draft updated","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]}},"required":["version"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions/{versionId}/products/{productId}":{"put":{"tags":["Price Lists"],"operationId":"upsertPriceListProductRule","summary":"Set a draft product rule","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"productId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0},"rule":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["fixed"]},"fixedPrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["type","fixedPrice"]},{"type":"object","properties":{"type":{"type":"string","enum":["discount"]},"discountPercent":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["type","discountPercent"]}]}},"required":["expectedRevision","rule"]}}}},"responses":{"200":{"description":"Rule saved","content":{"application/json":{"schema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListVersionId":{"type":"string"},"targetKind":{"type":"string","enum":["product","bundle"]},"productId":{"type":["string","null"]},"bundleId":{"type":["string","null"]},"fixedPrice":{"type":["string","null"]},"discountPercent":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListVersionId","targetKind","productId","bundleId","fixedPrice","discountPercent","createdAt","updatedAt"]},"revision":{"type":"integer"}},"required":["rule","revision"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Price Lists"],"operationId":"deletePriceListProductRule","summary":"Delete a draft product rule","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"productId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0}},"required":["expectedRevision"]}}}},"responses":{"200":{"description":"Rule deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"revision":{"type":"integer"}},"required":["deleted","revision"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions/{versionId}/bundles/{bundleId}":{"put":{"tags":["Price Lists"],"operationId":"upsertPriceListBundleRule","summary":"Set a draft bundle rule","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"bundleId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0},"rule":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["fixed"]},"fixedPrice":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["type","fixedPrice"]},{"type":"object","properties":{"type":{"type":"string","enum":["discount"]},"discountPercent":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"}},"required":["type","discountPercent"]}]}},"required":["expectedRevision","rule"]}}}},"responses":{"200":{"description":"Rule saved","content":{"application/json":{"schema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListVersionId":{"type":"string"},"targetKind":{"type":"string","enum":["product","bundle"]},"productId":{"type":["string","null"]},"bundleId":{"type":["string","null"]},"fixedPrice":{"type":["string","null"]},"discountPercent":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListVersionId","targetKind","productId","bundleId","fixedPrice","discountPercent","createdAt","updatedAt"]},"revision":{"type":"integer"}},"required":["rule","revision"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}},"delete":{"tags":["Price Lists"],"operationId":"deletePriceListBundleRule","summary":"Delete a draft bundle rule","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"bundleId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0}},"required":["expectedRevision"]}}}},"responses":{"200":{"description":"Rule deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"revision":{"type":"integer"}},"required":["deleted","revision"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions/{versionId}/publish":{"post":{"tags":["Price Lists"],"operationId":"publishPriceListVersion","summary":"Publish an immutable effective-dated version","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0},"activation":{"oneOf":[{"type":"object","properties":{"mode":{"type":"string","enum":["now"]}},"required":["mode"]},{"type":"object","properties":{"mode":{"type":"string","enum":["scheduled"]},"effectiveFrom":{"type":["string","null"],"format":"date-time"}},"required":["mode","effectiveFrom"]}]},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"supersedesVersionId":{"type":"string","minLength":1}},"required":["expectedRevision","activation"]}}}},"responses":{"200":{"description":"Version published","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]}},"required":["version"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions/{versionId}/cancel":{"post":{"tags":["Price Lists"],"operationId":"cancelPriceListVersion","summary":"Cancel a future publication","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0}},"required":["expectedRevision"]}}}},"responses":{"200":{"description":"Publication cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]}},"required":["version"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/{priceListId}/versions/{versionId}/end":{"post":{"tags":["Price Lists"],"operationId":"endPriceListVersion","summary":"Shorten an active or future publication","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"priceListId","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"versionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0},"mode":{"type":"string","enum":["now"]}},"required":["expectedRevision","mode"]},{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":0},"mode":{"type":"string","enum":["scheduled"]},"effectiveUntil":{"type":["string","null"],"format":"date-time"}},"required":["expectedRevision","mode","effectiveUntil"]}]}}}},"responses":{"200":{"description":"Publication shortened","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"priceListId":{"type":"string"},"currency":{"type":"string"},"publicationNumber":{"type":["integer","null"]},"defaultDiscountPercent":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"effectiveFrom":{"type":["string","null"],"format":"date-time"},"effectiveUntil":{"type":["string","null"],"format":"date-time"},"createdBy":{"type":"string"},"publishedAt":{"type":["string","null"],"format":"date-time"},"publishedBy":{"type":["string","null"]},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelledBy":{"type":["string","null"]},"endedEarlyAt":{"type":["string","null"],"format":"date-time"},"endedEarlyBy":{"type":["string","null"]},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","labId","priceListId","currency","publicationNumber","defaultDiscountPercent","revision","effectiveFrom","effectiveUntil","createdBy","publishedAt","publishedBy","cancelledAt","cancelledBy","endedEarlyAt","endedEarlyBy","createdAt","updatedAt"]}},"required":["version"]}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/partnerships/{partnershipId}":{"put":{"tags":["Price Lists"],"operationId":"assignPartnershipPriceList","summary":"Assign a stable price book to a partnership","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"partnershipId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"priceListId":{"type":["string","null"],"minLength":1}},"required":["priceListId"]}}}},"responses":{"200":{"description":"Partnership assignment updated","content":{"application/json":{"schema":{"type":"object","properties":{"partnership":{}}}}}},"400":{"description":"Invalid pricing lifecycle request"},"401":{"description":"Unauthorized"},"403":{"description":"Only lab managers may manage price lists"},"404":{"description":"Price-list resource not found"},"409":{"description":"Pricing lifecycle conflict"},"500":{"description":"Internal server error"}}}},"/price-lists/preview":{"post":{"tags":["Price Lists"],"operationId":"previewOrderPricing","summary":"Preview authoritative pricing without pinning an order","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1},"clinicId":{"type":"string","minLength":1},"patientId":{"type":["string","null"],"minLength":1},"items":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","minLength":1},"toothGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","enum":["single","bridge"]},"teeth":{"type":"array","items":{"type":"object","properties":{"fdi":{"type":"integer"},"role":{"type":"string","enum":["crown","pontic"]},"support":{"type":"string","enum":["natural","implant"]},"implantSystemId":{"type":"string"},"implantPlatform":{"type":"string","maxLength":50}},"required":["fdi","role"]},"minItems":1}},"required":["id","type","teeth"]}},"quantity":{"type":"integer","exclusiveMinimum":0,"default":1},"materialId":{"type":"string"},"material":{"type":"string"},"shadeSystemId":{"type":"string"},"shadeOcclusal":{"type":"string"},"shadeMiddle":{"type":"string"},"shadeGingival":{"type":"string"},"shadeNotes":{"type":"string","maxLength":5000},"defaultImplantSystemId":{"type":"string"},"defaultImplantPlatform":{"type":"string","maxLength":50},"notes":{"type":"string","maxLength":5000},"customFieldValues":{"type":"object","additionalProperties":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"fileIds":{"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false}},"bundleId":{"type":"string"},"bundleInstanceId":{"type":"string"}},"required":["productId"],"additionalProperties":false},"minItems":1,"maxItems":100}},"required":["labId","clinicId","items"]}}}},"responses":{"200":{"description":"Authoritative pricing preview","content":{"application/json":{"schema":{"type":"object","properties":{"effectiveAt":{"type":["string","null"],"format":"date-time"},"selection":{"type":"string","enum":["assigned_price_list","lab_default_price_list","catalog"]},"priceListId":{"type":["string","null"]},"priceListVersionId":{"type":["string","null"]},"currency":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"unitPrice":{"type":["string","null"]},"totalPrice":{"type":["string","null"]},"pricingSource":{"type":"string","enum":["fixed_rule","percent_rule","version_default_discount","catalog"]},"priceListVersionId":{"type":["string","null"]},"priceListRuleId":{"type":["string","null"]},"priceListRuleTargetKind":{"type":["string","null"],"enum":["product","bundle",null]},"priceListProductRuleId":{"type":["string","null"]},"priceListBundleRuleId":{"type":["string","null"]}},"required":["unitPrice","totalPrice","pricingSource","priceListVersionId","priceListRuleId","priceListRuleTargetKind","priceListProductRuleId","priceListBundleRuleId"]}}},"required":["effectiveAt","selection","priceListId","priceListVersionId","currency","items"]}}}},"400":{"description":"Catalog validation failed"},"401":{"description":"Unauthorized"},"403":{"description":"No active partnership access"},"404":{"description":"Lab not found"},"500":{"description":"Internal server error"}}}},"/production/settings":{"get":{"tags":["Production"],"operationId":"getProductionSettings","summary":"Get production settings","description":"Retrieves the production configuration settings for the current lab. Returns an unpersisted default projection when no row exists; writes occur only through the manager-guarded update command.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Lab production settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"isEnabled":{"type":"boolean"},"cutoffTime":{"type":"string"},"bufferDays":{"type":"number"},"showPricesInOrderDetails":{"type":"boolean"},"taskPageHeaders":{"type":["object","null"],"additionalProperties":{"type":"boolean"}},"excludedCategoryIds":{"type":["array","null"],"items":{"type":"string"}},"excludedProductIds":{"type":["array","null"],"items":{"type":"string"}},"useDoneDate":{"type":"boolean"},"defaultUnitCapacity":{"type":"number"},"enableBarcodeScanning":{"type":"boolean"},"organizeByDate":{"type":"boolean"},"defaultSortField":{"type":"string","enum":["scheduledDate","dueDate","orderNumber","priority"]},"defaultGroupBy":{"type":"string","enum":["room","assignee","status","date"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","isEnabled","cutoffTime","bufferDays","showPricesInOrderDetails","taskPageHeaders","excludedCategoryIds","excludedProductIds","useDoneDate","defaultUnitCapacity","enableBarcodeScanning","organizeByDate","defaultSortField","defaultGroupBy","createdAt","updatedAt"],"additionalProperties":{}}},"required":["settings"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}},"put":{"tags":["Production"],"operationId":"updateProductionSettings","summary":"Update production settings","description":"Updates the production configuration settings for the current lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isEnabled":{"type":"boolean"},"cutoffTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"bufferDays":{"type":"integer","minimum":0,"maximum":30},"showPricesInOrderDetails":{"type":"boolean"},"taskPageHeaders":{"type":"object","properties":{"showClinic":{"type":"boolean","default":true},"showPatient":{"type":"boolean","default":true},"showOrderNumber":{"type":"boolean","default":true},"showDueDate":{"type":"boolean","default":true},"showProduct":{"type":"boolean","default":true}}},"excludedCategoryIds":{"type":"array","items":{"type":"string"}},"excludedProductIds":{"type":"array","items":{"type":"string"}},"useDoneDate":{"type":"boolean"},"defaultUnitCapacity":{"type":"integer","minimum":1,"maximum":1000},"enableBarcodeScanning":{"type":"boolean"},"organizeByDate":{"type":"boolean"},"defaultSortField":{"type":"string","enum":["scheduledDate","dueDate","orderNumber","priority"]},"defaultGroupBy":{"type":"string","enum":["room","assignee","status","date"]}}}}}},"responses":{"200":{"description":"Settings updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"isEnabled":{"type":"boolean"},"cutoffTime":{"type":"string"},"bufferDays":{"type":"number"},"showPricesInOrderDetails":{"type":"boolean"},"taskPageHeaders":{"type":["object","null"],"additionalProperties":{"type":"boolean"}},"excludedCategoryIds":{"type":["array","null"],"items":{"type":"string"}},"excludedProductIds":{"type":["array","null"],"items":{"type":"string"}},"useDoneDate":{"type":"boolean"},"defaultUnitCapacity":{"type":"number"},"enableBarcodeScanning":{"type":"boolean"},"organizeByDate":{"type":"boolean"},"defaultSortField":{"type":"string","enum":["scheduledDate","dueDate","orderNumber","priority"]},"defaultGroupBy":{"type":"string","enum":["room","assignee","status","date"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","isEnabled","cutoffTime","bufferDays","showPricesInOrderDetails","taskPageHeaders","excludedCategoryIds","excludedProductIds","useDoneDate","defaultUnitCapacity","enableBarcodeScanning","organizeByDate","defaultSortField","defaultGroupBy","createdAt","updatedAt"],"additionalProperties":{}}},"required":["settings"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}}},"/production/rooms":{"get":{"tags":["Production"],"operationId":"listProductionRooms","summary":"List production rooms","description":"Retrieves a paginated list of production rooms for the current lab. Supports filtering by active status and search by name.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of production rooms","content":{"application/json":{"schema":{"type":"object","properties":{"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":"string"},"icon":{"type":["string","null"]},"layoutType":{"type":"string","enum":["default","acceptance","shipping","production"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","color","icon","layoutType","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["rooms"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}},"post":{"tags":["Production"],"operationId":"createProductionRoom","summary":"Create a production room","description":"Creates a new production room in the current lab. Automatically assigns the next sort order if not specified.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","default":"#6366f1"},"icon":{"type":"string","maxLength":50},"layoutType":{"type":"string","enum":["default","acceptance","shipping","production"],"default":"default"},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Room created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"room":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":"string"},"icon":{"type":["string","null"]},"layoutType":{"type":"string","enum":["default","acceptance","shipping","production"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","color","icon","layoutType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["room"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}}},"/production/rooms/reorder":{"put":{"tags":["Production"],"operationId":"reorderProductionRooms","summary":"Reorder production rooms","description":"Updates the sort order of multiple production rooms in a single batch operation.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"sortOrder":{"type":"integer","minimum":0}},"required":["id","sortOrder"]}}},"required":["items"]}}}},"responses":{"200":{"description":"Rooms reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}}},"/production/rooms/{id}":{"get":{"tags":["Production"],"operationId":"getProductionRoom","summary":"Get a production room","description":"Retrieves a single production room by its ID. Only returns rooms belonging to the current lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Room ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Room ID","name":"id","in":"path"}],"responses":{"200":{"description":"Room details","content":{"application/json":{"schema":{"type":"object","properties":{"room":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":"string"},"icon":{"type":["string","null"]},"layoutType":{"type":"string","enum":["default","acceptance","shipping","production"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","color","icon","layoutType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["room"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Room not found"}}},"put":{"tags":["Production"],"operationId":"updateProductionRoom","summary":"Update a production room","description":"Updates an existing production room's properties such as name, description, color, icon, sort order, or active status.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Room ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Room ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","default":"#6366f1"},"icon":{"type":"string","maxLength":50},"layoutType":{"type":"string","enum":["default","acceptance","shipping","production"],"default":"default"},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Room updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"room":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":"string"},"icon":{"type":["string","null"]},"layoutType":{"type":"string","enum":["default","acceptance","shipping","production"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","color","icon","layoutType","sortOrder","isActive","createdAt","updatedAt"]}},"required":["room"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Room not found"}}},"delete":{"tags":["Production"],"operationId":"deleteProductionRoom","summary":"Delete a production room","description":"Permanently deletes a production room. Tasks assigned to this room will need to be reassigned.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Room ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Room ID","name":"id","in":"path"}],"responses":{"200":{"description":"Room deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Room not found"},"409":{"description":"Room is still used by an active order"}}}},"/production/rooms/{id}/orders":{"get":{"tags":["Production"],"operationId":"listRoomRuntimeOrders","summary":"List orders assigned to a production room","description":"Returns the orders relevant to a room together with the per-task done-ness (computed from orderActivity).","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"Zirconia"},"required":false,"name":"material","in":"query"},{"schema":{"type":"string","example":"2026-04-21","description":"Optional YYYY-MM-DD filter. When provided, only orders with at least one task scheduled for this date in the given room are returned."},"required":false,"description":"Optional YYYY-MM-DD filter. When provided, only orders with at least one task scheduled for this date in the given room are returned.","name":"scheduledDate","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false,"description":"When true, only return orders whose current task in this room resolves to the current member (per-order override if present, otherwise the task's defaultAssigneeMemberId)."},"required":false,"description":"When true, only return orders whose current task in this room resolves to the current member (per-order override if present, otherwise the task's defaultAssigneeMemberId).","name":"onlyMine","in":"query"}],"responses":{"200":{"description":"Orders + tasks","content":{"application/json":{"schema":{"type":"object","properties":{"orders":{"type":"array","items":{"type":"object","properties":{"orderPhaseId":{"type":"string"},"treatmentPhaseId":{"type":"string"},"phaseName":{"type":"string"},"phaseSortOrder":{"type":"integer"},"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"patient":{"type":["object","null"],"properties":{"firstName":{"type":"string"},"lastName":{"type":"string"}},"required":["firstName","lastName"]},"clinic":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"dentist":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"productName":{"type":["string","null"]},"material":{"type":["string","null"]},"quantity":{"type":"number"}},"required":["id","productName","material","quantity"]}},"currentPhaseTaskId":{"type":["string","null"]},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"color":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"]},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]}},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}},"roomId":{"type":["string","null"]},"sortOrder":{"type":"integer"},"advancesPhase":{"type":"boolean"},"done":{"type":"boolean"},"isCurrent":{"type":"boolean"},"assignedToMemberId":{"type":["string","null"]},"scheduledDate":{"type":["string","null"]},"notes":{"type":["string","null"]}},"required":["id","name","description","icon","color","style","completionTrigger","completionTriggerConfig","sideEffects","captureConfig","roomId","sortOrder","advancesPhase","done","isCurrent","assignedToMemberId","scheduledDate","notes"]}},"phaseTasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sortOrder":{"type":"integer"},"task":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":["string","null"]},"icon":{"type":["string","null"]}},"required":["name","color","icon"]}},"required":["id","sortOrder","task"]}},"progress":{"type":"object","properties":{"completed":{"type":"number"},"total":{"type":"number"}},"required":["completed","total"]},"hasOverdueTask":{"type":"boolean"},"trayId":{"type":["string","null"]},"trayCode":{"type":["string","null"]},"trayColor":{"type":["string","null"]},"submittedAt":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"dueDate":{"type":["string","null"]},"plannedDate":{"type":["string","null"]}},"required":["orderPhaseId","treatmentPhaseId","phaseName","phaseSortOrder","id","orderNumber","status","patient","clinic","dentist","items","currentPhaseTaskId","tasks","phaseTasks","progress","hasOverdueTask","trayId","trayCode","trayColor","submittedAt","dueDate","plannedDate"]}}},"required":["orders"]}}}},"404":{"description":"Room not found"}}}},"/production/rooms/{id}/schedule-counts":{"get":{"tags":["Production"],"operationId":"getRoomScheduleCounts","summary":"Per-day scheduled-stage counts for a production room","description":"Returns a map of YYYY-MM-DD → number of distinct active stages with a task scheduled that day in the room. Powers the production date picker's per-day badges.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"2026-06-29"},"required":true,"name":"startDate","in":"query"},{"schema":{"type":"string","example":"2026-08-03"},"required":true,"name":"endDate","in":"query"}],"responses":{"200":{"description":"Per-day counts","content":{"application/json":{"schema":{"type":"object","properties":{"counts":{"type":"object","additionalProperties":{"type":"number"},"example":{"2026-07-04":3,"2026-07-05":1},"description":"Map of YYYY-MM-DD → number of distinct active stages scheduled in the room that day."}},"required":["counts"]}}}},"404":{"description":"Room not found"}}}},"/production/printers":{"get":{"tags":["Production"],"operationId":"listPrinters","summary":"List printers","description":"Retrieves the list of printers for the current lab. Joins room and current batch.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"roomId","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of printers","content":{"application/json":{"schema":{"type":"object","properties":{"printers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"model":{"type":["string","null"]},"material":{"type":"string"},"status":{"type":"string","enum":["idle","running","finished","error","maintenance"]},"currentBatchId":{"type":["string","null"]},"sortOrder":{"type":["number","null"]},"isActive":{"type":["boolean","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":["string","null"]}},"required":["id","name","color"]},"currentBatch":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"startedAt":{"type":["string","null"]},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId"]}}},"required":["id","name","status","startedAt"]}},"required":["id","labId","roomId","name","model","material","status","currentBatchId","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["printers"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}},"post":{"tags":["Production"],"operationId":"createPrinter","summary":"Create a printer","description":"Creates a new printer. The assigned room must belong to the current lab and have layoutType='production'.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"roomId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":100},"model":{"type":["string","null"],"maxLength":100},"material":{"type":"string","maxLength":100,"default":""},"status":{"type":"string","enum":["idle","finished","error","maintenance"],"default":"idle"},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["roomId","name"]}}}},"responses":{"201":{"description":"Printer created","content":{"application/json":{"schema":{"type":"object","properties":{"printer":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"model":{"type":["string","null"]},"material":{"type":"string"},"status":{"type":"string","enum":["idle","running","finished","error","maintenance"]},"currentBatchId":{"type":["string","null"]},"sortOrder":{"type":["number","null"]},"isActive":{"type":["boolean","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":["string","null"]}},"required":["id","name","color"]},"currentBatch":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"startedAt":{"type":["string","null"]},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId"]}}},"required":["id","name","status","startedAt"]}},"required":["id","labId","roomId","name","model","material","status","currentBatchId","sortOrder","isActive","createdAt","updatedAt"]}},"required":["printer"]}}}},"400":{"description":"Assigned room is not a production room or other validation error"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Room not found"}}}},"/production/printers/{id}":{"get":{"tags":["Production"],"operationId":"getPrinter","summary":"Get a printer","description":"Retrieves a single printer by ID within the current lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Printer ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Printer ID","name":"id","in":"path"}],"responses":{"200":{"description":"Printer","content":{"application/json":{"schema":{"type":"object","properties":{"printer":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"model":{"type":["string","null"]},"material":{"type":"string"},"status":{"type":"string","enum":["idle","running","finished","error","maintenance"]},"currentBatchId":{"type":["string","null"]},"sortOrder":{"type":["number","null"]},"isActive":{"type":["boolean","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":["string","null"]}},"required":["id","name","color"]},"currentBatch":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"startedAt":{"type":["string","null"]},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId"]}}},"required":["id","name","status","startedAt"]}},"required":["id","labId","roomId","name","model","material","status","currentBatchId","sortOrder","isActive","createdAt","updatedAt"]}},"required":["printer"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Printer not found"}}},"put":{"tags":["Production"],"operationId":"updatePrinter","summary":"Update a printer","description":"Updates a printer. If roomId changes, the new room must be a production room in the current lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Printer ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Printer ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"roomId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":100},"model":{"type":["string","null"],"maxLength":100},"material":{"type":"string","maxLength":100,"default":""},"status":{"type":"string","enum":["idle","finished","error","maintenance"],"default":"idle"},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Printer updated","content":{"application/json":{"schema":{"type":"object","properties":{"printer":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"model":{"type":["string","null"]},"material":{"type":"string"},"status":{"type":"string","enum":["idle","running","finished","error","maintenance"]},"currentBatchId":{"type":["string","null"]},"sortOrder":{"type":["number","null"]},"isActive":{"type":["boolean","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":["string","null"]}},"required":["id","name","color"]},"currentBatch":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"startedAt":{"type":["string","null"]},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId"]}}},"required":["id","name","status","startedAt"]}},"required":["id","labId","roomId","name","model","material","status","currentBatchId","sortOrder","isActive","createdAt","updatedAt"]}},"required":["printer"]}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Printer not found"},"409":{"description":"Printer state changed concurrently"}}},"delete":{"tags":["Production"],"operationId":"deletePrinter","summary":"Delete a printer","description":"Permanently deletes a printer. Rejected while a print batch is active.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Printer ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Printer ID","name":"id","in":"path"}],"responses":{"200":{"description":"Printer deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Printer has an active batch"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Printer not found"}}}},"/production/print-batches":{"get":{"tags":["Production"],"operationId":"listPrintBatches","summary":"List print batches","description":"Retrieves print batches for the current lab. Filter by printerId, roomId, or status. Joins order items.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"printerId","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"roomId","in":"query"},{"schema":{"type":"string","enum":["queued","printing","completed","failed"],"example":"queued"},"required":false,"name":"status","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"description":"Return only batches that are queued or printing, newest first.","example":"true"},"required":false,"description":"Return only batches that are queued or printing, newest first.","name":"activeOnly","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Opaque cursor returned by the previous page. Pages are ordered by creation time and ID, newest first."},"required":false,"description":"Opaque cursor returned by the previous page. Pages are ordered by creation time and ID, newest first.","name":"cursor","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":100,"description":"Maximum batches to return","example":100},"required":false,"description":"Maximum batches to return","name":"limit","in":"query"}],"responses":{"200":{"description":"List of print batches","content":{"application/json":{"schema":{"type":"object","properties":{"batches":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"printerId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["queued","printing","completed","failed"]},"startedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"createdByMemberId":{"type":["string","null"]},"completedByMemberId":{"type":["string","null"]},"notes":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId","orderNumber","productName","material"]}},"orderItemsTruncated":{"type":"boolean"}},"required":["id","labId","printerId","roomId","name","status","startedAt","completedAt","createdByMemberId","completedByMemberId","notes","failureReason","durationSeconds","createdAt","updatedAt","orderItemsTruncated"]}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["limit","hasMore","nextCursor"]}},"required":["batches","pagination"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}},"post":{"tags":["Production"],"operationId":"createPrintBatch","summary":"Create a print batch","description":"Creates a new print batch in queued state. Verifies printer is idle with no current batch and that all order items belong to the lab. Generates a sequential name Batch-YYYYMMDD-XX.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"printerId":{"type":"string","minLength":1},"orderItemIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":200},"notes":{"type":"string","maxLength":1000}},"required":["printerId","orderItemIds"]}}}},"responses":{"201":{"description":"Print batch created","content":{"application/json":{"schema":{"type":"object","properties":{"batch":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"printerId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["queued","printing","completed","failed"]},"startedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"createdByMemberId":{"type":["string","null"]},"completedByMemberId":{"type":["string","null"]},"notes":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId","orderNumber","productName","material"]}},"orderItemsTruncated":{"type":"boolean"}},"required":["id","labId","printerId","roomId","name","status","startedAt","completedAt","createdByMemberId","completedByMemberId","notes","failureReason","durationSeconds","createdAt","updatedAt","orderItemsTruncated"]}},"required":["batch"]}}}},"400":{"description":"Printer not idle, inactive, or order items invalid"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Printer not found"}}}},"/production/print-batches/{id}/start":{"post":{"tags":["Production"],"operationId":"startPrintBatch","summary":"Start a print batch","description":"Transitions a queued batch to printing. Printer running state is derived from the active batch.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Print batch ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Print batch ID","name":"id","in":"path"}],"responses":{"200":{"description":"Batch started","content":{"application/json":{"schema":{"type":"object","properties":{"batch":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"printerId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["queued","printing","completed","failed"]},"startedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"createdByMemberId":{"type":["string","null"]},"completedByMemberId":{"type":["string","null"]},"notes":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId","orderNumber","productName","material"]}},"orderItemsTruncated":{"type":"boolean"}},"required":["id","labId","printerId","roomId","name","status","startedAt","completedAt","createdByMemberId","completedByMemberId","notes","failureReason","durationSeconds","createdAt","updatedAt","orderItemsTruncated"]}},"required":["batch"]}}}},"400":{"description":"Batch is not in a startable state"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Batch not found"},"409":{"description":"Batch or printer state changed concurrently"}}}},"/production/print-batches/{id}/complete":{"post":{"tags":["Production"],"operationId":"completePrintBatch","summary":"Complete a print batch","description":"Transitions a printing batch to completed and leaves the printer finished until a human clears it.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Print batch ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Print batch ID","name":"id","in":"path"}],"responses":{"200":{"description":"Batch completed","content":{"application/json":{"schema":{"type":"object","properties":{"batch":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"printerId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["queued","printing","completed","failed"]},"startedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"createdByMemberId":{"type":["string","null"]},"completedByMemberId":{"type":["string","null"]},"notes":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId","orderNumber","productName","material"]}},"orderItemsTruncated":{"type":"boolean"}},"required":["id","labId","printerId","roomId","name","status","startedAt","completedAt","createdByMemberId","completedByMemberId","notes","failureReason","durationSeconds","createdAt","updatedAt","orderItemsTruncated"]},"advanced":{"type":"array","items":{"type":"string"}},"skipped":{"type":"array","items":{"type":"object","properties":{"orderId":{"type":"string"},"reason":{"type":"string"}},"required":["orderId","reason"]}},"requiresManualReview":{"type":"boolean"}},"required":["batch","requiresManualReview"]}}}},"400":{"description":"Batch is not in a completable state"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Batch not found"},"409":{"description":"Batch or printer state changed concurrently"}}}},"/production/print-batches/{id}/fail":{"post":{"tags":["Production"],"operationId":"failPrintBatch","summary":"Mark a print batch as failed","description":"Transitions a printing batch to failed, captures the reason, and leaves the printer in error until a human clears it.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Print batch ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Print batch ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"description":"Batch marked failed","content":{"application/json":{"schema":{"type":"object","properties":{"batch":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"printerId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["queued","printing","completed","failed"]},"startedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"createdByMemberId":{"type":["string","null"]},"completedByMemberId":{"type":["string","null"]},"notes":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId","orderNumber","productName","material"]}},"orderItemsTruncated":{"type":"boolean"}},"required":["id","labId","printerId","roomId","name","status","startedAt","completedAt","createdByMemberId","completedByMemberId","notes","failureReason","durationSeconds","createdAt","updatedAt","orderItemsTruncated"]}},"required":["batch"]}}}},"400":{"description":"Batch is not in a failable state"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Batch not found"},"409":{"description":"Batch or printer state changed concurrently"}}}},"/production/print-batches/{id}":{"get":{"tags":["Production"],"operationId":"getPrintBatch","summary":"Get a print batch","description":"Retrieves a single batch by ID with its order items.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Print batch ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Print batch ID","name":"id","in":"path"}],"responses":{"200":{"description":"Batch","content":{"application/json":{"schema":{"type":"object","properties":{"batch":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"printerId":{"type":"string"},"roomId":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["queued","printing","completed","failed"]},"startedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"createdByMemberId":{"type":["string","null"]},"completedByMemberId":{"type":["string","null"]},"notes":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"durationSeconds":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"orderItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":["string","null"]},"productName":{"type":["string","null"]},"material":{"type":["string","null"]}},"required":["id","orderId","orderNumber","productName","material"]}},"orderItemsTruncated":{"type":"boolean"}},"required":["id","labId","printerId","roomId","name","status","startedAt","completedAt","createdByMemberId","completedByMemberId","notes","failureReason","durationSeconds","createdAt","updatedAt","orderItemsTruncated"]}},"required":["batch"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Batch not found"}}},"delete":{"tags":["Production"],"operationId":"deletePrintBatch","summary":"Delete a print batch","description":"Deletes a print batch. Only allowed if status is queued.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Print batch ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Print batch ID","name":"id","in":"path"}],"responses":{"200":{"description":"Batch deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Batch is not in a deletable state"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Batch not found"}}}},"/production/print-batches/history":{"get":{"tags":["Production"],"operationId":"listPrintBatchHistory","summary":"List completed and failed print batches","description":"Paginated history of batches with status completed or failed. Joins printer, room, and member display names. Ordered by completedAt DESC.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"printerId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"roomId","in":"query"},{"schema":{"type":"string","enum":["completed","failed"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string"},"required":false,"name":"operatorId","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"required":false,"name":"to","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"required":false,"name":"pageSize","in":"query"}],"responses":{"200":{"description":"History page","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["items","total","page","pageSize"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}}},"/production/print-batches/printers/{printerId}/history-stats":{"get":{"tags":["Production"],"operationId":"getPrinterHistoryStats","summary":"Aggregate stats for a printer over a trailing window","description":"Total runtime, average duration, and success rate over the last N days (default 30). Based on completed+failed batches.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Printer ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Printer ID","name":"printerId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":365,"default":30},"required":false,"name":"windowDays","in":"query"}],"responses":{"200":{"description":"Aggregate stats"},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Printer not found"}}}},"/production/tasks":{"get":{"tags":["Production"],"operationId":"listProductionTasks","summary":"List production tasks","description":"Returns the task library for the current lab. Filter by trigger, active flag, or free-text name search.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"isActive","in":"query"},{"schema":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"required":false,"name":"completionTrigger","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"List of tasks","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"iconDefault":{"type":["string","null"]},"colorDefault":{"type":["string","null"]},"styleDefault":{"type":"string","enum":["primary","secondary","danger","default"]},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}},"defaultAssigneeMemberId":{"type":["string","null"]},"roomId":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","iconDefault","colorDefault","styleDefault","completionTrigger","completionTriggerConfig","sideEffects","captureConfig","defaultAssigneeMemberId","roomId","isActive","createdAt","updatedAt"]}}},"required":["tasks"]}}}}}},"post":{"tags":["Production"],"operationId":"createProductionTask","summary":"Create a task","description":"Create a new task definition in the lab's task library.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":["string","null"],"maxLength":2000},"iconDefault":{"type":["string","null"]},"colorDefault":{"type":["string","null"],"pattern":"^#[0-9A-Fa-f]{6}$"},"styleDefault":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}},"default":{"requireNotes":false,"requirePhoto":false}},"defaultAssigneeMemberId":{"type":["string","null"]},"roomId":{"type":["string","null"]},"isActive":{"type":"boolean","default":true}},"required":["name","completionTrigger","completionTriggerConfig"]}}}},"responses":{"201":{"description":"Task created","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"iconDefault":{"type":["string","null"]},"colorDefault":{"type":["string","null"]},"styleDefault":{"type":"string","enum":["primary","secondary","danger","default"]},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}},"defaultAssigneeMemberId":{"type":["string","null"]},"roomId":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","iconDefault","colorDefault","styleDefault","completionTrigger","completionTriggerConfig","sideEffects","captureConfig","defaultAssigneeMemberId","roomId","isActive","createdAt","updatedAt"]}},"required":["task"]}}}}}}},"/production/tasks/{id}":{"get":{"tags":["Production"],"operationId":"getProductionTask","summary":"Get a task","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Task","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"iconDefault":{"type":["string","null"]},"colorDefault":{"type":["string","null"]},"styleDefault":{"type":"string","enum":["primary","secondary","danger","default"]},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}},"defaultAssigneeMemberId":{"type":["string","null"]},"roomId":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","iconDefault","colorDefault","styleDefault","completionTrigger","completionTriggerConfig","sideEffects","captureConfig","defaultAssigneeMemberId","roomId","isActive","createdAt","updatedAt"]}},"required":["task"]}}}},"404":{"description":"Task not found"}}},"patch":{"tags":["Production"],"operationId":"updateProductionTask","summary":"Update a task","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":["string","null"],"maxLength":2000},"iconDefault":{"type":["string","null"]},"colorDefault":{"type":["string","null"],"pattern":"^#[0-9A-Fa-f]{6}$"},"styleDefault":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}},"default":{"requireNotes":false,"requirePhoto":false}},"defaultAssigneeMemberId":{"type":["string","null"]},"roomId":{"type":["string","null"]},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Updated task","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"iconDefault":{"type":["string","null"]},"colorDefault":{"type":["string","null"]},"styleDefault":{"type":"string","enum":["primary","secondary","danger","default"]},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}},"defaultAssigneeMemberId":{"type":["string","null"]},"roomId":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","iconDefault","colorDefault","styleDefault","completionTrigger","completionTriggerConfig","sideEffects","captureConfig","defaultAssigneeMemberId","roomId","isActive","createdAt","updatedAt"]}},"required":["task"]}}}},"404":{"description":"Task not found"},"409":{"description":"The update would leave an active treatment phase without an active task","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["Production"],"operationId":"deleteProductionTask","summary":"Delete a task","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Task not found"},"409":{"description":"Task is still used by a treatment phase","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/production/tasks/bulk":{"post":{"tags":["Production"],"operationId":"bulkUpdateProductionTasks","summary":"Bulk update tasks","description":"Activate, deactivate, set the default assignee, or delete several tasks at once. Only affects tasks owned by the current lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","enum":["activate"]},"taskIds":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["action","taskIds"]},{"type":"object","properties":{"action":{"type":"string","enum":["deactivate"]},"taskIds":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["action","taskIds"]},{"type":"object","properties":{"action":{"type":"string","enum":["delete"]},"taskIds":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["action","taskIds"]},{"type":"object","properties":{"action":{"type":"string","enum":["assign"]},"taskIds":{"type":"array","items":{"type":"string"},"minItems":1},"defaultAssigneeMemberId":{"type":["string","null"]}},"required":["action","taskIds","defaultAssigneeMemberId"]}]}}}},"responses":{"200":{"description":"Bulk result","content":{"application/json":{"schema":{"type":"object","properties":{"affected":{"type":"number"}},"required":["affected"]}}}},"409":{"description":"A task is still referenced or deactivation would leave an active phase without a task","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/production/tasks/{orderPhaseTaskId}/fire":{"post":{"tags":["Production"],"operationId":"fireProductionTask","summary":"Fire a task (runs trigger + side effects, logs activity)","description":"Single entry point for completing an immutable order phase task snapshot.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"orderPhaseTaskId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string"},"orderPhaseId":{"type":"string"},"decisionKey":{"type":"string"},"trayId":{"type":"string"},"fileIds":{"type":"array","items":{"type":"string"}},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"manualFilePath":{"type":"string","maxLength":2000},"commandId":{"type":"string","format":"uuid"},"reportFileIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":20},"extraFileIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":20},"projectUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"notes":{"type":"string","maxLength":5000},"attachmentIds":{"type":"array","items":{"type":"string"}},"confirmed":{"type":"boolean"}},"required":["orderId","orderPhaseId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Activity row recorded","content":{"application/json":{"schema":{"type":"object","properties":{"activity":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderPhaseTaskId":{"type":["string","null"]},"eventType":{"type":"string","enum":["task_fired","task_review_event","status_changed","phase_changed","tray_assigned","file_uploaded","note_added","system","hold_requirement_changed"]},"actorMemberId":{"type":["string","null"]},"actorUserId":{"type":["string","null"]},"phaseAtTime":{"type":["string","null"]},"statusAtTime":{"type":["string","null"],"enum":["draft","submitted","active","completed","cancelled","on_hold",null]},"payload":{"type":"object","additionalProperties":{}},"notes":{"type":["string","null"]},"attachmentIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"}},"required":["id","orderId","orderPhaseTaskId","eventType","actorMemberId","actorUserId","phaseAtTime","statusAtTime","payload","notes","attachmentIds","createdAt"]}},"required":["activity"]}}}},"400":{"description":"Invalid trigger input"},"404":{"description":"Task or order not found"},"409":{"description":"Confirmation required"}}}},"/production/task-assignments":{"post":{"tags":["Production"],"operationId":"updateOrderPhaseTaskAssignment","summary":"Set per-order assignee/date/notes for a task","description":"Updates assignee, date, or notes on the immutable task row for an order phase.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"orderPhaseTaskId":{"type":"string"},"orderId":{"type":"string"},"orderPhaseId":{"type":"string"},"assignedToMemberId":{"type":["string","null"]},"scheduledDate":{"type":["string","null"]},"notes":{"type":["string","null"],"maxLength":2000}},"required":["orderPhaseTaskId","orderId","orderPhaseId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Updated assignment","content":{"application/json":{"schema":{"type":"object","properties":{"assignment":{"type":"object","properties":{"orderPhaseTaskId":{"type":"string"},"orderId":{"type":"string"},"orderPhaseId":{"type":"string"},"assignedToMemberId":{"type":["string","null"]},"scheduledDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["orderPhaseTaskId","orderId","orderPhaseId","assignedToMemberId","scheduledDate","notes","createdAt","updatedAt"]}},"required":["assignment"]}}}}}},"delete":{"tags":["Production"],"operationId":"clearOrderPhaseTaskAssignment","summary":"Clear assignment fields for an order phase task","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"orderPhaseTaskId":{"type":"string"},"orderId":{"type":"string"},"orderPhaseId":{"type":"string"}},"required":["orderPhaseTaskId","orderId","orderPhaseId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Cleared","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}}}}},"/production/orders/{id}/activity":{"get":{"tags":["Production"],"operationId":"listOrderActivity","summary":"Cronologia: list activity rows for an order","description":"Returns activity rows ordered by createdAt DESC. Powers the Cronologia rail and acts as the source of truth for the per-phase-visit done-ness calculation.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["task_fired","task_review_event","status_changed","phase_changed","tray_assigned","file_uploaded","note_added","system","hold_requirement_changed"]},"required":false,"name":"eventType","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Activity rows","content":{"application/json":{"schema":{"type":"object","properties":{"activity":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderPhaseTaskId":{"type":["string","null"]},"eventType":{"type":"string","enum":["task_fired","task_review_event","status_changed","phase_changed","tray_assigned","file_uploaded","note_added","system","hold_requirement_changed"]},"actorMemberId":{"type":["string","null"]},"actorUserId":{"type":["string","null"]},"phaseAtTime":{"type":["string","null"]},"statusAtTime":{"type":["string","null"],"enum":["draft","submitted","active","completed","cancelled","on_hold",null]},"payload":{"type":"object","additionalProperties":{}},"notes":{"type":["string","null"]},"attachmentIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"}},"required":["id","orderId","orderPhaseTaskId","eventType","actorMemberId","actorUserId","phaseAtTime","statusAtTime","payload","notes","attachmentIds","createdAt"]}}},"required":["activity"]}}}},"404":{"description":"Order not found"}}}},"/production/orders/{id}/task-plan":{"get":{"tags":["Production"],"operationId":"getOrderTaskPlan","summary":"Get the task plan for an order","description":"Returns every task applicable to this order grouped by phase → room → task, with assignment overrides and done-ness.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Task plan","content":{"application/json":{"schema":{"type":"object","properties":{"phases":{"type":"array","items":{"type":"object","properties":{"orderPhaseId":{"type":"string"},"treatmentPhaseId":{"type":"string"},"name":{"type":"string"},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"workTrayId":{"type":["string","null"]},"rooms":{"type":"array","items":{"type":"object","properties":{"roomId":{"type":"string"},"name":{"type":"string"},"color":{"type":["string","null"]},"icon":{"type":["string","null"]},"sortOrder":{"type":"number"},"layoutType":{"type":"string"},"tasks":{"type":"array","items":{"type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"color":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"]},"completionTrigger":{"type":"string","enum":["acknowledge","assign_tray","upload_file","change_status","decision","set_manual_file_path","surgical_report"]},"completionTriggerConfig":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["acknowledge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_tray"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["upload_file"]},"category":{"type":"string","enum":["scan","photo","prescription","xray","surgical_report","other"]},"multiple":{"type":"boolean"}},"required":["type","category"]},{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"allowedStatuses":{"type":"array","items":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]},"minItems":1}},"required":["type","allowedStatuses"]},{"type":"object","properties":{"type":{"type":"string","enum":["decision"]},"options":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":80},"icon":{"type":["string","null"]},"style":{"type":"string","enum":["primary","secondary","danger","default"],"default":"default"},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]},"maxItems":5,"default":[]},"capture":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}}},"required":["key","label"]},"minItems":2,"maxItems":6}},"required":["type","options"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_manual_file_path"]},"placeholder":{"type":"string","maxLength":200}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["surgical_report"]},"bookingUrl":{"type":["string","null"],"maxLength":2048,"format":"uri"},"instructions":{"type":["string","null"],"maxLength":2000}},"required":["type"]}]},"sideEffects":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["change_status"]},"toStatus":{"type":"string","enum":["draft","submitted","active","completed","cancelled","on_hold"]}},"required":["type","toStatus"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_tray"]},"trayCode":{"type":"string","minLength":1}},"required":["type","trayCode"]},{"type":"object","properties":{"type":{"type":"string","enum":["update_delivery_date"]},"offsetDays":{"type":"integer"}},"required":["type","offsetDays"]},{"type":"object","properties":{"type":{"type":"string","enum":["send_email"]},"templateId":{"type":"string","minLength":1},"to":{"type":"string","enum":["clinic","patient","custom"]},"customEmail":{"type":"string","format":"email"}},"required":["type","templateId","to"]},{"type":"object","properties":{"type":{"type":"string","enum":["pause_order"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["resume_order"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["show_dialog"]},"text":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text"]}]}},"captureConfig":{"type":"object","properties":{"requireNotes":{"type":"boolean","default":false},"requirePhoto":{"type":"boolean","default":false},"notesPlaceholder":{"type":"string"},"notesMaxLength":{"type":"integer","minimum":1,"maximum":5000}}},"roomId":{"type":["string","null"]},"sortOrder":{"type":"integer"},"advancesPhase":{"type":"boolean"},"done":{"type":"boolean"},"isCurrent":{"type":"boolean"},"assignedToMemberId":{"type":["string","null"]},"scheduledDate":{"type":["string","null"]},"notes":{"type":["string","null"]}},"required":["id","name","description","icon","color","style","completionTrigger","completionTriggerConfig","sideEffects","captureConfig","roomId","sortOrder","advancesPhase","done","isCurrent","assignedToMemberId","scheduledDate","notes"]}},"required":["task"]}}},"required":["roomId","name","color","icon","sortOrder","layoutType","tasks"]}}},"required":["orderPhaseId","treatmentPhaseId","name","sortOrder","isActive","workTrayId","rooms"]}}},"required":["phases"]}}}},"404":{"description":"Order not found"}}}},"/production/activity":{"get":{"tags":["Production"],"operationId":"listProductionActivityFeed","summary":"Storico: lab-wide activity feed","description":"Returns activity rows across all orders in the current lab, joined with order/member/user/task metadata. Supports event-type filter and createdAt cursor-based pagination.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":365},"required":false,"name":"days","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$","description":"ISO timestamp of the last item returned in the previous page. Items with createdAt < cursor are returned."},"required":false,"description":"ISO timestamp of the last item returned in the previous page. Items with createdAt < cursor are returned.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["task_fired","task_review_event","status_changed","phase_changed","tray_assigned","file_uploaded","note_added","system","hold_requirement_changed"]},"required":false,"name":"eventType","in":"query"}],"responses":{"200":{"description":"Activity feed items","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":"string"},"eventType":{"type":"string","enum":["task_fired","task_review_event","status_changed","phase_changed","tray_assigned","file_uploaded","note_added","system","hold_requirement_changed"]},"taskName":{"type":["string","null"]},"actorName":{"type":"string"},"isSystemActor":{"type":"boolean"},"actorKind":{"type":"string","enum":["system","user","anonymized_user"]},"createdAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$"},"payload":{"type":"object","additionalProperties":{}},"phaseAtTime":{"type":["string","null"]},"statusAtTime":{"type":["string","null"],"enum":["draft","submitted","active","completed","cancelled","on_hold",null]}},"required":["id","orderId","orderNumber","eventType","taskName","actorName","isSystemActor","actorKind","createdAt","payload","phaseAtTime","statusAtTime"]}},"nextCursor":{"type":"string"}},"required":["items"]}}}}}}},"/production/teammates":{"get":{"tags":["Production"],"operationId":"listProductionTeammates","summary":"List production teammates","description":"Retrieves a paginated list of lab members with their production room assignments. Supports filtering by room and search by name or email.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"roomId","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of teammates with room assignments","content":{"application/json":{"schema":{"type":"object","properties":{"teammates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"image":{"type":["string","null"]}},"required":["id","name","email","image"]},"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string"},"roomId":{"type":"string"},"canView":{"type":"boolean"},"canWork":{"type":"boolean"},"canAssign":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","color","createdAt","updatedAt"],"additionalProperties":{}}},"required":["id","memberId","roomId","canView","canWork","canAssign","createdAt","updatedAt","room"]}}},"required":["id","userId","role","user","rooms"]}}},"required":["teammates"]}}}},"401":{"description":"Unauthorized — missing or invalid session"}}}},"/production/teammates/{memberId}/rooms":{"get":{"tags":["Production"],"operationId":"getTeammateRoomAssignments","summary":"Get teammate room assignments","description":"Retrieves all production room assignments for a specific lab member, including room details and permission flags (canView, canWork, canAssign).","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Member ID","example":"mem_123"},"required":true,"description":"Member ID","name":"memberId","in":"path"}],"responses":{"200":{"description":"List of room assignments with permissions","content":{"application/json":{"schema":{"type":"object","properties":{"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string"},"roomId":{"type":"string"},"canView":{"type":"boolean"},"canWork":{"type":"boolean"},"canAssign":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","color","createdAt","updatedAt"],"additionalProperties":{}}},"required":["id","memberId","roomId","canView","canWork","canAssign","createdAt","updatedAt","room"]}}},"required":["rooms"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Member not found"}}},"post":{"tags":["Production"],"operationId":"assignTeammateRoom","summary":"Assign a room to a teammate","description":"Assigns a production room to a lab member with configurable permissions (canView, canWork, canAssign). Returns a 409 if the member is already assigned to the room.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Member ID","example":"mem_123"},"required":true,"description":"Member ID","name":"memberId","in":"path"}],"responses":{"201":{"description":"Room assignment created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"assignment":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string"},"roomId":{"type":"string"},"canView":{"type":"boolean"},"canWork":{"type":"boolean"},"canAssign":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"room":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","color","createdAt","updatedAt"],"additionalProperties":{}}},"required":["id","memberId","roomId","canView","canWork","canAssign","createdAt","updatedAt","room"]}},"required":["assignment"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Member or room not found"},"409":{"description":"Member is already assigned to this room"}}},"put":{"tags":["Production"],"operationId":"updateTeammateRoomPermissions","summary":"Update teammate room permissions","description":"Updates a member's permissions for a specific production room assignment, including canView, canWork, and canAssign flags.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Member ID","example":"mem_123"},"required":true,"description":"Member ID","name":"memberId","in":"path"}],"responses":{"200":{"description":"Room permissions updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"assignment":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string"},"roomId":{"type":"string"},"canView":{"type":"boolean"},"canWork":{"type":"boolean"},"canAssign":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","memberId","roomId","canView","canWork","canAssign","createdAt","updatedAt"]}},"required":["assignment"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Member or room assignment not found"}}},"delete":{"tags":["Production"],"operationId":"removeTeammateRoomAssignment","summary":"Remove a teammate room assignment","description":"Removes a production room assignment from a lab member. The roomId must be provided in the request body.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Member ID","example":"mem_123"},"required":true,"description":"Member ID","name":"memberId","in":"path"}],"responses":{"200":{"description":"Room assignment removed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Member or room assignment not found"}}}},"/production/teammates/{memberId}/settings":{"get":{"tags":["Production"],"operationId":"getTeammateSettings","summary":"Get teammate production settings","description":"Retrieves production-specific settings for a lab member, including daily unit capacity, access level, default room, and unassigned task visibility. Returns defaults if no settings are configured.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Member ID","example":"mem_123"},"required":true,"description":"Member ID","name":"memberId","in":"path"}],"responses":{"200":{"description":"Member production settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"memberId":{"type":"string"},"dailyUnitCapacity":{"type":"number"},"accessLevel":{"type":"string"},"showUnassignedTasks":{"type":"boolean"},"defaultRoomId":{"type":["string","null"]},"defaultRoom":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","color","createdAt","updatedAt"]}},"required":["memberId","dailyUnitCapacity","accessLevel","showUnassignedTasks","defaultRoomId","defaultRoom"],"additionalProperties":{}}},"required":["settings"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Member not found"}}},"put":{"tags":["Production"],"operationId":"updateTeammateSettings","summary":"Update teammate production settings","description":"Creates or updates production-specific settings for a lab member. Upserts the settings record, creating one with defaults if it does not exist yet.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Member ID","example":"mem_123"},"required":true,"description":"Member ID","name":"memberId","in":"path"}],"responses":{"200":{"description":"Member settings updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string"},"dailyUnitCapacity":{"type":"number"},"accessLevel":{"type":"string"},"showUnassignedTasks":{"type":"boolean"},"defaultRoomId":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","memberId","dailyUnitCapacity","accessLevel","showUnassignedTasks","defaultRoomId","createdAt","updatedAt"],"additionalProperties":{}}},"required":["settings"]}}}},"401":{"description":"Unauthorized — missing or invalid session"},"404":{"description":"Member not found"}}}},"/production/statistics/overview":{"get":{"tags":["Production"],"operationId":"getProductionStatisticsOverview","summary":"Get production statistics overview","description":"Returns a minimal production statistics overview computed from the activity log. Detailed per-room / per-member breakdowns are TODO under the new task model.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"}],"responses":{"200":{"description":"Statistics overview","content":{"application/json":{"schema":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalTasks":{"type":"number"},"completedTasks":{"type":"number"},"inProgressTasks":{"type":"number"},"pendingTasks":{"type":"number"},"averageCompletionTime":{"type":["number","null"]},"capacityUsage":{"type":"number"}},"required":["totalTasks","completedTasks","inProgressTasks","pendingTasks","averageCompletionTime","capacityUsage"]},"byRoom":{"type":"array","items":{},"default":[]},"byMember":{"type":"array","items":{},"default":[]},"dailyTrend":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"completed":{"type":"number"},"total":{"type":"number"}},"required":["date","completed","total"]}},"dateRange":{"type":"object","properties":{"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["startDate","endDate"]}},"required":["overview","dailyTrend","dateRange"]}}}},"409":{"description":"Organization timezone is not a valid IANA zone","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["ORGANIZATION_CONFIGURATION_INVALID"]}},"required":["error","code"]}}}}}}},"/inventory/categories":{"get":{"tags":["Inventory"],"operationId":"listInventoryCategories","summary":"List inventory categories","description":"Returns all inventory categories for the current organization, ordered by sort order and name. Optionally includes inactive categories.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Set to 'true' to include inactive categories","example":"false"},"required":false,"description":"Set to 'true' to include inactive categories","name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of inventory categories","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","parentId","name","description","icon","sortOrder","isActive","createdAt","updatedAt"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"post":{"tags":["Inventory"],"operationId":"createInventoryCategory","summary":"Create an inventory category","description":"Creates a new inventory category for organizing inventory items. Supports hierarchical parent-child relationships.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"parentId":{"type":"string"},"icon":{"type":"string","maxLength":50},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["name"]}}}},"responses":{"201":{"description":"Category created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","parentId","name","description","icon","sortOrder","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"}}}},"/inventory/categories/reorder":{"post":{"tags":["Inventory"],"operationId":"reorderInventoryCategories","summary":"Reorder inventory categories","description":"Updates the sort order of inventory categories based on the provided ordered list of category IDs. All IDs must belong to the current organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryIds":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["categoryIds"]}}}},"responses":{"200":{"description":"Categories reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"One or more categories not found"}}}},"/inventory/categories/seed":{"post":{"tags":["Inventory"],"operationId":"seedInventoryCategories","summary":"Seed default inventory categories","description":"Populates the organization with preset inventory categories based on the organization type (lab or clinic). This is idempotent and will not re-seed if already done.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Categories already seeded","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"alreadySeeded":{"type":"boolean"},"seeded":{"type":"number"}}}},"required":["data"]}}}},"201":{"description":"Default categories seeded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"seeded":{"type":"number"}},"required":["seeded"]}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/inventory/categories/{id}":{"get":{"tags":["Inventory"],"operationId":"getInventoryCategory","summary":"Get an inventory category","description":"Retrieves the full details of a single inventory category by its ID.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","parentId","name","description","icon","sortOrder","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Category not found"}}},"put":{"tags":["Inventory"],"operationId":"updateInventoryCategory","summary":"Update an inventory category","description":"Updates the properties of an existing inventory category such as name, description, icon, or active status.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"parentId":{"type":"string"},"icon":{"type":"string","maxLength":50},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Updated category","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"parentId":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"sortOrder":{"type":"number"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","parentId","name","description","icon","sortOrder","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Category not found"}}},"delete":{"tags":["Inventory"],"operationId":"deleteInventoryCategory","summary":"Delete an inventory category","description":"Permanently deletes an inventory category. Items in this category will lose their category association.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Category ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Category ID","name":"id","in":"path"}],"responses":{"200":{"description":"Category deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Category not found"}}}},"/inventory/items":{"get":{"tags":["Inventory"],"operationId":"listInventoryItems","summary":"List inventory items","description":"Returns a paginated list of inventory items for the current organization. Supports filtering by category, supplier, low stock, expiring soon, and search by name or SKU.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"categoryId","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"supplierId","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"lowStock","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"expiringSoon","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string","enum":["name","sku","currentQuantity","unitCost","expirationDate","createdAt"],"default":"name","example":"name"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc","example":"asc"},"required":false,"name":"sortOrder","in":"query"}],"responses":{"200":{"description":"Paginated list of inventory items with category and supplier names","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"categoryId":{"type":["string","null"]},"supplierId":{"type":["string","null"]},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"unit":{"type":"string"},"currentQuantity":{"type":"string"},"minQuantity":{"type":["string","null"]},"maxQuantity":{"type":["string","null"]},"unitCost":{"type":["string","null"]},"currency":{"type":"string"},"storageLocation":{"type":["string","null"]},"lotNumber":{"type":["string","null"]},"expirationDate":{"type":["string","null"]},"barcode":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"notes":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"categoryName":{"type":["string","null"]},"supplierName":{"type":["string","null"]}},"required":["id","organizationId","categoryId","supplierId","name","sku","description","unit","currentQuantity","minQuantity","maxQuantity","unitCost","currency","storageLocation","lotNumber","expirationDate","barcode","imageKey","notes","isActive","createdAt","updatedAt","categoryName","supplierName"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - valid session required"}}},"post":{"tags":["Inventory"],"operationId":"createInventoryItem","summary":"Create an inventory item","description":"Creates a new inventory item in the organization's catalog. If the initial quantity is greater than zero, an initial stock adjustment is automatically recorded.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sku":{"type":"string","maxLength":50},"description":{"type":"string","maxLength":2000},"categoryId":{"type":"string"},"supplierId":{"type":"string"},"unit":{"type":"string","enum":["pcs","g","kg","ml","L","box","roll","pack","set","pair","sheet","tube","bottle","cartridge","syringe","capsule"],"default":"pcs"},"currentQuantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"}],"default":0},"minQuantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"},{"type":"null"}]},"maxQuantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"},{"type":"null"}]},"unitCost":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"default":"GBP"},"storageLocation":{"type":"string","maxLength":200},"lotNumber":{"type":"string","maxLength":100},"expirationDate":{"type":["string","null"]},"barcode":{"type":"string","maxLength":100},"notes":{"type":"string","maxLength":2000}},"required":["name"]}}}},"responses":{"201":{"description":"Inventory item created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"unit":{"type":"string"},"currentQuantity":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","sku","unit","currentQuantity","isActive","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"An item with this SKU already exists"}}}},"/inventory/items/{id}":{"get":{"tags":["Inventory"],"operationId":"getInventoryItem","summary":"Get an inventory item","description":"Retrieves the full details of a single inventory item by its ID, including category name.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Inventory item ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Inventory item ID","name":"id","in":"path"}],"responses":{"200":{"description":"Inventory item details with category name","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"categoryId":{"type":["string","null"]},"supplierId":{"type":["string","null"]},"name":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"]},"unit":{"type":"string"},"currentQuantity":{"type":"string"},"minQuantity":{"type":["string","null"]},"maxQuantity":{"type":["string","null"]},"unitCost":{"type":["string","null"]},"currency":{"type":"string"},"storageLocation":{"type":["string","null"]},"lotNumber":{"type":["string","null"]},"expirationDate":{"type":["string","null"]},"barcode":{"type":["string","null"]},"imageKey":{"type":["string","null"]},"notes":{"type":["string","null"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"categoryName":{"type":["string","null"]}},"required":["id","organizationId","categoryId","supplierId","name","sku","description","unit","currentQuantity","minQuantity","maxQuantity","unitCost","currency","storageLocation","lotNumber","expirationDate","barcode","imageKey","notes","isActive","createdAt","updatedAt","categoryName"],"additionalProperties":{}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Item not found"}}},"put":{"tags":["Inventory"],"operationId":"updateInventoryItem","summary":"Update an inventory item","description":"Updates the metadata of an existing inventory item. Does not change the current quantity - use stock adjustments for that.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Inventory item ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Inventory item ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sku":{"type":"string","maxLength":50},"description":{"type":"string","maxLength":2000},"categoryId":{"type":"string"},"supplierId":{"type":"string"},"unit":{"type":"string","enum":["pcs","g","kg","ml","L","box","roll","pack","set","pair","sheet","tube","bottle","cartridge","syringe","capsule"],"default":"pcs"},"currentQuantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"}],"default":0},"minQuantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"},{"type":"null"}]},"maxQuantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"},{"type":"null"}]},"unitCost":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"default":"GBP"},"storageLocation":{"type":"string","maxLength":200},"lotNumber":{"type":"string","maxLength":100},"expirationDate":{"type":["string","null"]},"barcode":{"type":"string","maxLength":100},"notes":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"description":"Updated inventory item","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"unit":{"type":"string"},"currentQuantity":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","sku","unit","currentQuantity","isActive","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Item not found"},"409":{"description":"An item with this SKU already exists"}}},"delete":{"tags":["Inventory"],"operationId":"archiveInventoryItem","summary":"Archive an inventory item","description":"Soft-deletes an inventory item by marking it as inactive and setting the archived timestamp. The item remains in the database for historical reference.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Inventory item ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Inventory item ID","name":"id","in":"path"}],"responses":{"200":{"description":"Item archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Item not found"}}}},"/inventory/items/{id}/image":{"get":{"tags":["Inventory"],"operationId":"getInventoryItemImage","summary":"Get an inventory item image","description":"Serves the managed image attached to an inventory item.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Inventory item image binary"},"404":{"description":"Item or image not found"}}},"post":{"tags":["Inventory"],"operationId":"uploadInventoryItemImage","summary":"Upload an inventory item image","description":"Uploads or replaces the managed image for an inventory item.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Inventory item image attached","content":{"application/json":{"schema":{"type":"object","properties":{"imageKey":{"type":"string"}},"required":["imageKey"]}}}},"400":{"description":"Invalid image"},"404":{"description":"Item not found"}}},"delete":{"tags":["Inventory"],"operationId":"deleteInventoryItemImage","summary":"Delete an inventory item image","description":"Detaches an inventory item image and queues object deletion.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Inventory item image removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Item or image not found"}}}},"/inventory/suppliers":{"get":{"tags":["Inventory"],"operationId":"listSuppliers","summary":"List suppliers","description":"Returns a paginated list of suppliers for the current organization. Supports search by name, code, or email and optional inclusion of inactive suppliers.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"Paginated list of suppliers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"contactName":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"country":{"type":"string"},"currency":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","code","contactName","email","phone","website","country","currency","isActive","createdAt","updatedAt"],"additionalProperties":{}}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - valid session required"}}},"post":{"tags":["Inventory"],"operationId":"createSupplier","summary":"Create a supplier","description":"Creates a new supplier record for the organization, including contact details, payment terms, and delivery preferences.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"code":{"type":"string","maxLength":50},"contactName":{"type":"string","maxLength":200},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"phone":{"type":"string","maxLength":50},"website":{"type":"string","maxLength":500},"address":{"type":"string","maxLength":500},"addressLine2":{"type":"string","maxLength":500},"city":{"type":"string","maxLength":100},"postcode":{"type":"string","maxLength":20},"country":{"type":"string","default":"GB"},"paymentTerms":{"type":"string","maxLength":100},"leadTimeDays":{"type":["integer","null"],"minimum":0},"minimumOrderValue":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"default":"GBP"},"notes":{"type":"string","maxLength":2000}},"required":["name"]}}}},"responses":{"201":{"description":"Supplier created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"country":{"type":"string"},"currency":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","code","country","currency","isActive","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"A supplier with this code already exists"}}}},"/inventory/suppliers/{id}":{"get":{"tags":["Inventory"],"operationId":"getSupplier","summary":"Get a supplier","description":"Retrieves the full details of a single supplier by its ID.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Supplier ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Supplier ID","name":"id","in":"path"}],"responses":{"200":{"description":"Supplier details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"contactName":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"country":{"type":"string"},"currency":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","code","contactName","email","phone","website","country","currency","isActive","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Supplier not found"}}},"put":{"tags":["Inventory"],"operationId":"updateSupplier","summary":"Update a supplier","description":"Updates the details of an existing supplier including contact information, payment terms, and delivery preferences.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Supplier ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Supplier ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"code":{"type":"string","maxLength":50},"contactName":{"type":"string","maxLength":200},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"string","enum":[""]}]},"phone":{"type":"string","maxLength":50},"website":{"type":"string","maxLength":500},"address":{"type":"string","maxLength":500},"addressLine2":{"type":"string","maxLength":500},"city":{"type":"string","maxLength":100},"postcode":{"type":"string","maxLength":20},"country":{"type":"string","default":"GB"},"paymentTerms":{"type":"string","maxLength":100},"leadTimeDays":{"type":["integer","null"],"minimum":0},"minimumOrderValue":{"type":["string","null"],"pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"default":"GBP"},"notes":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"description":"Updated supplier","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"country":{"type":"string"},"currency":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","code","country","currency","isActive","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Supplier not found"},"409":{"description":"A supplier with this code already exists"}}},"delete":{"tags":["Inventory"],"operationId":"archiveSupplier","summary":"Archive a supplier","description":"Soft-deletes a supplier by marking it as inactive and setting the archived timestamp. The supplier remains in the database for historical reference.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Supplier ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Supplier ID","name":"id","in":"path"}],"responses":{"200":{"description":"Supplier archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Supplier not found"}}}},"/inventory/adjustments":{"get":{"tags":["Inventory"],"operationId":"listStockAdjustments","summary":"List stock adjustments","description":"Returns a paginated list of stock adjustment history for the current organization. Supports filtering by item, reason, and date range.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"inventoryItemId","in":"query"},{"schema":{"type":"string","example":"manual_add"},"required":false,"name":"reason","in":"query"},{"schema":{"type":"string","enum":["manual","order_consumption","order_reversal","purchase_receipt","system"]},"required":false,"name":"source","in":"query"},{"schema":{"type":"string","example":"2025-01-15"},"required":false,"name":"dateFrom","in":"query"},{"schema":{"type":"string","example":"2025-01-15"},"required":false,"name":"dateTo","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of stock adjustments with item and user names","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"inventoryItemId":{"type":"string"},"quantityBefore":{"type":"string"},"quantityChange":{"type":"string"},"quantityAfter":{"type":"string"},"source":{"type":"string","enum":["manual","order_consumption","order_reversal","purchase_receipt","system"]},"reason":{"type":"string"},"notes":{"type":["string","null"]},"orderId":{"type":["string","null"]},"orderItemId":{"type":["string","null"]},"purchaseOrderId":{"type":["string","null"]},"purchaseOrderItemId":{"type":["string","null"]},"purchaseOrderReceiptId":{"type":["string","null"]},"lotNumber":{"type":["string","null"]},"expirationDate":{"type":["string","null"]},"reversesAdjustmentId":{"type":["string","null"]},"adjustedBy":{"type":"string"},"createdAt":{"type":"string"},"itemName":{"type":["string","null"]},"adjustedByName":{"type":["string","null"]}},"required":["id","inventoryItemId","quantityBefore","quantityChange","quantityAfter","source","reason","notes","orderId","orderItemId","purchaseOrderId","purchaseOrderItemId","purchaseOrderReceiptId","lotNumber","expirationDate","reversesAdjustmentId","adjustedBy","createdAt","itemName","adjustedByName"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - valid session required"}}},"post":{"tags":["Inventory"],"operationId":"createStockAdjustment","summary":"Create a stock adjustment","description":"Records a manual stock adjustment for an inventory item. The quantity change can be positive (adding stock) or negative (removing stock). The item's current quantity is updated atomically.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"inventoryItemId":{"type":"string","minLength":1},"quantityChange":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"}]},"reason":{"type":"string","minLength":1},"notes":{"type":"string","maxLength":1000},"orderId":{"type":"string"},"orderItemId":{"type":"string"}},"required":["inventoryItemId","quantityChange","reason"]}}}},"responses":{"201":{"description":"Stock adjustment recorded and item quantity updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"inventoryItemId":{"type":"string"},"organizationId":{"type":"string"},"quantityBefore":{"type":"string"},"quantityChange":{"type":"string"},"quantityAfter":{"type":"string"},"source":{"type":"string","enum":["manual"]},"reason":{"type":"string"},"notes":{"type":["string","null"]},"adjustedBy":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","inventoryItemId","organizationId","quantityBefore","quantityChange","quantityAfter","source","reason","notes","adjustedBy","createdAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body or stock cannot go below zero"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Inventory item not found"}}}},"/inventory/adjustment-reasons":{"get":{"tags":["Inventory"],"operationId":"listAdjustmentReasons","summary":"List adjustment reasons","description":"Returns all stock adjustment reasons for the current organization, ordered by sort order and label. Optionally includes inactive reasons.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Set to 'true' to include inactive reasons","example":"false"},"required":false,"description":"Set to 'true' to include inactive reasons","name":"includeInactive","in":"query"}],"responses":{"200":{"description":"List of adjustment reasons","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"key":{"type":"string"},"label":{"type":"string"},"isSystem":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","key","label","isSystem","isActive","sortOrder","createdAt","updatedAt"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"post":{"tags":["Inventory"],"operationId":"createAdjustmentReason","summary":"Create an adjustment reason","description":"Creates a custom stock adjustment reason for the organization. A unique key is auto-generated from the label if not provided.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":100},"key":{"type":"string","maxLength":100}},"required":["label"]}}}},"responses":{"201":{"description":"Adjustment reason created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"key":{"type":"string"},"label":{"type":"string"},"isSystem":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","key","label","isSystem","isActive","sortOrder","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request body or could not generate a valid key"},"401":{"description":"Unauthorized - valid session required"},"409":{"description":"A reason with this key already exists"}}}},"/inventory/adjustment-reasons/reorder":{"post":{"tags":["Inventory"],"operationId":"reorderAdjustmentReasons","summary":"Reorder adjustment reasons","description":"Updates the sort order of adjustment reasons based on the provided ordered list of reason IDs. All IDs must belong to the current organization.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reasonIds":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["reasonIds"]}}}},"responses":{"200":{"description":"Reasons reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"One or more reasons not found"}}}},"/inventory/adjustment-reasons/seed":{"post":{"tags":["Inventory"],"operationId":"seedAdjustmentReasons","summary":"Seed default adjustment reasons","description":"Populates the organization with a default set of stock adjustment reasons (e.g. damaged, expired, manual correction). This is idempotent and will not re-seed if already done.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Reasons already seeded","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"alreadySeeded":{"type":"boolean"},"seeded":{"type":"number"}}}},"required":["data"]}}}},"201":{"description":"Default adjustment reasons seeded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"seeded":{"type":"number"}},"required":["seeded"]}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/inventory/adjustment-reasons/{id}":{"put":{"tags":["Inventory"],"operationId":"updateAdjustmentReason","summary":"Update an adjustment reason","description":"Updates the label of an existing stock adjustment reason.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Reason ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Reason ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1}},"required":["label"]}}}},"responses":{"200":{"description":"Updated adjustment reason","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"key":{"type":"string"},"label":{"type":"string"},"isSystem":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":["number","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","key","label","isSystem","isActive","sortOrder","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Reason not found"}}},"delete":{"tags":["Inventory"],"operationId":"deleteAdjustmentReason","summary":"Delete an adjustment reason","description":"Permanently deletes a custom stock adjustment reason. System-provided reasons cannot be deleted.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Reason ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Reason ID","name":"id","in":"path"}],"responses":{"200":{"description":"Reason deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized - valid session required"},"403":{"description":"System reasons cannot be deleted"},"404":{"description":"Reason not found"}}}},"/inventory/purchase-orders":{"get":{"tags":["Inventory"],"operationId":"listPurchaseOrders","summary":"List purchase orders","description":"Returns a paginated list of purchase orders for the current organization. Supports filtering by status and supplier.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":0,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["draft","sent","partially_received","received","cancelled"],"example":"draft"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"supplierId","in":"query"}],"responses":{"200":{"description":"Paginated list of purchase orders with supplier names","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"poNumber":{"type":"string"},"status":{"type":"string"},"supplierId":{"type":"string"},"subtotal":{"type":["string","null"]},"taxAmount":{"type":["string","null"]},"totalAmount":{"type":["string","null"]},"currency":{"type":"string"},"orderDate":{"type":["string","null"]},"expectedDeliveryDate":{"type":["string","null"]},"receivedDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"createdAt":{"type":"string"},"supplierName":{"type":["string","null"]}},"required":["id","poNumber","status","supplierId","subtotal","taxAmount","totalAmount","currency","orderDate","expectedDeliveryDate","receivedDate","notes","createdAt","supplierName"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized - valid session required"}}},"post":{"tags":["Inventory"],"operationId":"createPurchaseOrder","summary":"Create a purchase order","description":"Creates a new purchase order with line items for the specified supplier. Automatically generates a sequential PO number and calculates totals.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"supplierId":{"type":"string","minLength":1},"expectedDeliveryDate":{"type":"string"},"notes":{"type":"string","maxLength":2000},"internalNotes":{"type":"string","maxLength":2000},"items":{"type":"array","items":{"type":"object","properties":{"inventoryItemId":{"type":"string","minLength":1},"quantityOrdered":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"}]},"unitCost":{"type":"string","pattern":"^[+-]?\\d+(?:\\.\\d+)?$"},"notes":{"type":"string","maxLength":500}},"required":["inventoryItemId","quantityOrdered","unitCost"]},"minItems":1}},"required":["supplierId","items"]}}}},"responses":{"201":{"description":"Purchase order created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"poNumber":{"type":"string"},"status":{"type":"string"},"supplierId":{"type":"string"},"subtotal":{"type":["string","null"]},"totalAmount":{"type":["string","null"]},"currency":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","poNumber","status","supplierId","subtotal","totalAmount","currency","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Supplier not found"}}}},"/inventory/purchase-orders/{id}":{"get":{"tags":["Inventory"],"operationId":"getPurchaseOrder","summary":"Get a purchase order","description":"Retrieves the full details of a purchase order including all line items with their associated inventory item names, SKUs, and units.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Purchase order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Purchase order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Purchase order details with line items","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"poNumber":{"type":"string"},"status":{"type":"string"},"supplierId":{"type":"string"},"subtotal":{"type":["string","null"]},"taxAmount":{"type":["string","null"]},"totalAmount":{"type":["string","null"]},"currency":{"type":"string"},"orderDate":{"type":["string","null"]},"expectedDeliveryDate":{"type":["string","null"]},"receivedDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"createdBy":{"type":"string"},"createdAt":{"type":"string"},"supplierName":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"inventoryItemId":{"type":"string"},"quantityOrdered":{"type":"string"},"quantityReceived":{"type":"string"},"unitCost":{"type":"string"},"totalCost":{"type":"string"},"notes":{"type":["string","null"]},"itemName":{"type":["string","null"]},"itemSku":{"type":["string","null"]},"itemUnit":{"type":["string","null"]}},"required":["id","inventoryItemId","quantityOrdered","quantityReceived","unitCost","totalCost","notes","itemName","itemSku","itemUnit"]}}},"required":["id","poNumber","status","supplierId","subtotal","taxAmount","totalAmount","currency","orderDate","expectedDeliveryDate","receivedDate","notes","internalNotes","createdBy","createdAt","supplierName","items"]}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Purchase order not found"}}},"delete":{"tags":["Inventory"],"operationId":"deletePurchaseOrder","summary":"Delete a purchase order","description":"Permanently deletes a purchase order. Only draft orders can be deleted; sent or received orders must be cancelled instead.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Purchase order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Purchase order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Purchase order deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Only draft purchase orders can be deleted"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Purchase order not found"}}}},"/inventory/purchase-orders/{id}/send":{"post":{"tags":["Inventory"],"operationId":"sendPurchaseOrder","summary":"Send a purchase order","description":"Transitions a draft purchase order to 'sent' status and records the order date. Only draft orders can be sent.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Purchase order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Purchase order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Purchase order marked as sent","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"poNumber":{"type":"string"},"status":{"type":"string"},"orderDate":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","poNumber","status","orderDate","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Only draft orders can be sent"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Purchase order not found"}}}},"/inventory/purchase-orders/{id}/cancel":{"post":{"tags":["Inventory"],"operationId":"cancelPurchaseOrder","summary":"Cancel a purchase order","description":"Cancels a purchase order. Cannot cancel orders that are already received or cancelled.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Purchase order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Purchase order ID","name":"id","in":"path"}],"responses":{"200":{"description":"Purchase order cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"poNumber":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","poNumber","status","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Cannot cancel a received or already cancelled order"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Purchase order not found"}}}},"/inventory/purchase-orders/{id}/receive":{"post":{"tags":["Inventory"],"operationId":"receivePurchaseOrderItems","summary":"Receive purchase order items","description":"Records the receipt of items from a purchase order. Updates inventory quantities, creates stock adjustments, and transitions the PO status to 'partially_received' or 'received' based on whether all items are fully received.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Purchase order ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Purchase order ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string","minLength":1,"maxLength":200},"items":{"type":"array","items":{"type":"object","properties":{"purchaseOrderItemId":{"type":"string","minLength":1},"quantityReceived":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^([+-]?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$"}]},"lotNumber":{"type":"string","maxLength":100},"expirationDate":{"type":"string"}},"required":["purchaseOrderItemId","quantityReceived"]},"minItems":1}},"required":["commandId","items"]}}}},"responses":{"200":{"description":"Items received and inventory updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}},"required":["data"]}}}},"400":{"description":"Only sent or partially received orders can receive items"},"401":{"description":"Unauthorized - valid session required"},"404":{"description":"Purchase order not found"},"409":{"description":"Receipt request is already being processed"},"422":{"description":"commandId was reused with different receipt parameters"}}}},"/inventory/settings":{"get":{"tags":["Inventory"],"operationId":"getInventorySettings","summary":"Get inventory settings","description":"Retrieves the user-editable inventory settings for the current organization. Returns sensible defaults if no settings have been configured yet.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Inventory settings for the organization","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"lowStockAlertEnabled":{"type":"boolean"},"expirationAlertDays":{"type":"number"},"autoDeductOnOrderComplete":{"type":"boolean"}},"required":["lowStockAlertEnabled","expirationAlertDays","autoDeductOnOrderComplete"],"additionalProperties":{}}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}},"put":{"tags":["Inventory"],"operationId":"updateInventorySettings","summary":"Update inventory settings","description":"Creates or updates the inventory module settings for the current organization. Performs an upsert - creates the settings row if it does not exist yet.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"lowStockAlertEnabled":{"type":"boolean"},"expirationAlertDays":{"type":"integer","minimum":1,"maximum":365},"autoDeductOnOrderComplete":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated inventory settings","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"lowStockAlertEnabled":{"type":"boolean"},"expirationAlertDays":{"type":"number"},"autoDeductOnOrderComplete":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","lowStockAlertEnabled","expirationAlertDays","autoDeductOnOrderComplete","createdAt","updatedAt"],"additionalProperties":{}}},"required":["data"]}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - valid session required"}}}},"/inventory/dashboard/stats":{"get":{"tags":["Inventory"],"operationId":"getInventoryDashboardStats","summary":"Get inventory dashboard statistics","description":"Returns a comprehensive dashboard overview including total item count, total inventory value, low-stock and out-of-stock counts, expiring items, top 10 low-stock items, top 10 expiring items, recent stock adjustments, and category breakdown with values.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Inventory dashboard statistics and alerts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"totalItems":{"type":"number"},"totalValue":{"type":"string"},"lowStockCount":{"type":"number"},"outOfStockCount":{"type":"number"},"expiringCount":{"type":"number"},"expirationAlertDays":{"type":"number"},"lowStockItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"currentQuantity":{"type":"string"},"minQuantity":{"type":["string","null"]},"unit":{"type":"string"}},"required":["id","name","sku","currentQuantity","minQuantity","unit"]}},"expiringItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":["string","null"]},"expirationDate":{"type":["string","null"]},"currentQuantity":{"type":"string"},"unit":{"type":"string"}},"required":["id","name","sku","expirationDate","currentQuantity","unit"]}},"recentAdjustments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"inventoryItemId":{"type":"string"},"quantityChange":{"type":"string"},"source":{"type":"string","enum":["manual","order_consumption","order_reversal","purchase_receipt","system"]},"reason":{"type":"string"},"notes":{"type":["string","null"]},"createdAt":{"type":"string"},"itemName":{"type":["string","null"]},"adjustedByName":{"type":["string","null"]}},"required":["id","inventoryItemId","quantityChange","source","reason","notes","createdAt","itemName","adjustedByName"]}},"categoryBreakdown":{"type":"array","items":{"type":"object","properties":{"categoryId":{"type":["string","null"]},"categoryName":{"type":["string","null"]},"categoryDescription":{"type":["string","null"]},"categoryIcon":{"type":["string","null"]},"itemCount":{"type":"number"},"totalValue":{"type":"string"}},"required":["categoryId","categoryName","categoryDescription","categoryIcon","itemCount","totalValue"]}}},"required":["totalItems","totalValue","lowStockCount","outOfStockCount","expiringCount","expirationAlertDays","lowStockItems","expiringItems","recentAdjustments","categoryBreakdown"]}},"required":["data"]}}}},"401":{"description":"Unauthorized - valid session required"}}}},"/stripe-connect/oauth/start":{"post":{"operationId":"startStripeConnectOAuth","description":"Start a one-time Stripe Standard OAuth flow. The lab owner can sign into an existing Stripe account or create a new one on Stripe.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","maxLength":2048}}}}}},"responses":{"200":{"description":"Stripe Standard OAuth authorization URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"accountId":{"type":["string","null"]},"flow":{"type":"string","enum":["oauth"]}},"required":["url","accountId","flow"]}}}},"409":{"description":"The lab already has an active Stripe connection"},"500":{"description":"Stripe Connect OAuth is not configured"},"503":{"description":"Stripe environment is not configured safely"}}}},"/stripe-connect/onboard":{"post":{"operationId":"onboardStripeConnect","description":"Compatibility alias for the Stripe Standard OAuth start command. No account is created until the user selects or creates one on Stripe.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"deprecated":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","maxLength":2048}}}}}},"responses":{"200":{"description":"Stripe Standard OAuth authorization URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"accountId":{"type":["string","null"]},"flow":{"type":"string","enum":["oauth"]}},"required":["url","accountId","flow"]}}}},"409":{"description":"The lab already has an active Stripe connection"},"500":{"description":"Stripe Connect OAuth is not configured"},"503":{"description":"Stripe environment is not configured safely"}}}},"/stripe-connect/oauth/callback":{"get":{"operationId":"handleStripeConnectOAuthCallback","description":"Consume a lab-owner-bound OAuth state, exchange Stripe's one-time code, retain the connection generation, reconcile account requirements, and redirect safely to the app.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":false,"name":"code","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"state","in":"query"},{"schema":{"type":"string","maxLength":100},"required":false,"name":"error","in":"query"},{"schema":{"type":"string","maxLength":500},"required":false,"name":"error_description","in":"query"}],"responses":{"302":{"description":"Redirect to the same-origin lab finance settings view"},"401":{"description":"The initiating user session is unavailable"}}}},"/stripe-connect/status":{"get":{"operationId":"getStripeConnectStatus","description":"Return the cached active Stripe connection and its latest requirements snapshot without contacting Stripe.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Cached Stripe Connect status","content":{"application/json":{"schema":{"type":"object","properties":{"connected":{"type":"boolean"},"connectionId":{"type":["string","null"]},"accountId":{"type":["string","null"]},"accountType":{"type":["string","null"],"enum":["standard","express","custom",null]},"chargesEnabled":{"type":"boolean"},"payoutsEnabled":{"type":"boolean"},"onboardingComplete":{"type":"boolean"},"detailsSubmitted":{"type":"boolean"},"email":{"type":["string","null"]},"businessName":{"type":["string","null"]},"country":{"type":["string","null"]},"requirements":{"type":"object","properties":{"currentlyDue":{"type":"array","items":{"type":"string"}},"eventuallyDue":{"type":"array","items":{"type":"string"}},"pastDue":{"type":"array","items":{"type":"string"}},"pendingVerification":{"type":"array","items":{"type":"string"}},"disabledReason":{"type":["string","null"]},"currentDeadline":{"type":["string","null"],"format":"date-time"}},"required":["currentlyDue","eventuallyDue","pastDue","pendingVerification","disabledReason","currentDeadline"]},"connectedAt":{"type":["string","null"],"format":"date-time"},"lastProviderEventId":{"type":["string","null"]},"lastProviderEventAt":{"type":["string","null"],"format":"date-time"}},"required":["connected","connectionId","accountId","accountType","chargesEnabled","payoutsEnabled","onboardingComplete","detailsSubmitted","email","businessName","country","requirements","connectedAt","lastProviderEventId","lastProviderEventAt"]}}}}}}},"/stripe-connect/status/refresh":{"post":{"operationId":"refreshStripeConnectStatus","description":"Retrieve the active account from Stripe and reconcile its capabilities, onboarding state, and requirements snapshot.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Reconciled Stripe Connect status","content":{"application/json":{"schema":{"type":"object","properties":{"connected":{"type":"boolean"},"connectionId":{"type":["string","null"]},"accountId":{"type":["string","null"]},"accountType":{"type":["string","null"],"enum":["standard","express","custom",null]},"chargesEnabled":{"type":"boolean"},"payoutsEnabled":{"type":"boolean"},"onboardingComplete":{"type":"boolean"},"detailsSubmitted":{"type":"boolean"},"email":{"type":["string","null"]},"businessName":{"type":["string","null"]},"country":{"type":["string","null"]},"requirements":{"type":"object","properties":{"currentlyDue":{"type":"array","items":{"type":"string"}},"eventuallyDue":{"type":"array","items":{"type":"string"}},"pastDue":{"type":"array","items":{"type":"string"}},"pendingVerification":{"type":"array","items":{"type":"string"}},"disabledReason":{"type":["string","null"]},"currentDeadline":{"type":["string","null"],"format":"date-time"}},"required":["currentlyDue","eventuallyDue","pastDue","pendingVerification","disabledReason","currentDeadline"]},"connectedAt":{"type":["string","null"],"format":"date-time"},"lastProviderEventId":{"type":["string","null"]},"lastProviderEventAt":{"type":["string","null"],"format":"date-time"}},"required":["connected","connectionId","accountId","accountType","chargesEnabled","payoutsEnabled","onboardingComplete","detailsSubmitted","email","businessName","country","requirements","connectedAt","lastProviderEventId","lastProviderEventAt"]}}}},"500":{"description":"Stripe is not configured"},"502":{"description":"Stripe account status is temporarily unavailable"},"503":{"description":"Stripe environment is not configured safely"}}}},"/stripe-connect/dashboard-link":{"get":{"operationId":"getStripeConnectDashboardLink","description":"Return the Stripe Dashboard URL for Standard accounts, with legacy login-link support for Express accounts.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Stripe Dashboard URL and account type","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"type":{"type":"string"}},"required":["url","type"]}}}},"400":{"description":"No Stripe account is connected"},"502":{"description":"Stripe Dashboard is temporarily unavailable"}}}},"/stripe-connect/disconnect":{"post":{"operationId":"disconnectStripeConnect","description":"Revoke the Standard OAuth authorization and deactivate the current local connection while retaining its account and lifecycle history.","tags":["Stripe Connect"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Stripe account disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"No Stripe account is connected"},"409":{"description":"Disconnect is blocked while payment, autopay, or refund commands are unsettled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"blockers":{"type":"array","items":{"type":"string","enum":["payment_attempt","autopay_attempt","refund"]}}},"required":["error","blockers"]}}}},"500":{"description":"Stripe Connect OAuth is not configured"},"502":{"description":"Stripe rejected the disconnection"},"503":{"description":"Stripe environment is not configured safely"}}}},"/checkout/create-payment-intent":{"post":{"operationId":"createPaymentIntent","description":"Create a Stripe PaymentIntent for the authenticated clinic to pay invoices or an order basket. Clinic-only. Prices are resolved from the lab catalog.","tags":["Checkout"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1,"description":"Lab ID to pay"},"idempotencyKey":{"type":"string","minLength":8,"maxLength":200,"description":"Stable key for this payment attempt; reuse it only for an exact retry"},"invoiceIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":20},"items":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","minLength":1},"toothGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"type":{"type":"string","enum":["single","bridge"]},"teeth":{"type":"array","items":{"type":"object","properties":{"fdi":{"type":"integer"},"role":{"type":"string","enum":["crown","pontic"]},"support":{"type":"string","enum":["natural","implant"]},"implantSystemId":{"type":"string"},"implantPlatform":{"type":"string","maxLength":50}},"required":["fdi","role"]},"minItems":1}},"required":["id","type","teeth"]}},"quantity":{"type":"integer","exclusiveMinimum":0,"default":1},"materialId":{"type":"string"},"material":{"type":"string"},"shadeSystemId":{"type":"string"},"shadeOcclusal":{"type":"string"},"shadeMiddle":{"type":"string"},"shadeGingival":{"type":"string"},"shadeNotes":{"type":"string","maxLength":5000},"defaultImplantSystemId":{"type":"string"},"defaultImplantPlatform":{"type":"string","maxLength":50},"notes":{"type":"string","maxLength":5000},"customFieldValues":{"type":"object","additionalProperties":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"number"}},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"fileIds":{"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false}},"bundleId":{"type":"string"},"bundleInstanceId":{"type":"string"}},"required":["productId"],"additionalProperties":false},"minItems":1,"maxItems":100},"draftOrderId":{"type":"string","minLength":1,"maxLength":128}},"required":["labId","idempotencyKey"],"additionalProperties":false}}}},"responses":{"200":{"description":"PaymentIntent created with client secret for Stripe.js confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"clientSecret":{"type":["string","null"]},"paymentIntentId":{"type":"string"},"amount":{"type":"number"},"amountDisplay":{"type":"string"},"currency":{"type":"string"},"status":{"type":"string"},"convertedOrderId":{"type":["string","null"]},"taxAmount":{"type":["string","null"]},"taxMode":{"type":["string","null"],"enum":["none","exclusive","inclusive",null]},"taxIncludedInPrices":{"type":["boolean","null"]}},"required":["clientSecret","paymentIntentId","amount","amountDisplay","currency","status","convertedOrderId","taxAmount","taxMode","taxIncludedInPrices"]}}}},"400":{"description":"Invalid request, invoices not payable, or lab has not connected Stripe","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"object","properties":{"code":{"type":"string","enum":["payment_amount_exceeds_provider_limit"]},"maxMinor":{"type":"string","enum":["99999999"]},"currency":{"type":"string"}},"required":["code","maxMinor","currency"]}},"required":["error"]}}}},"409":{"description":"Payment attempt conflicts with an existing claim"},"500":{"description":"Stripe secret key not configured"}}}},"/checkout/confirm-order":{"post":{"operationId":"confirmCheckoutPayment","description":"Verify that a Stripe PaymentIntent has succeeded after the client-side payment flow completes. Clinic-only. Used as a server-side confirmation step before updating order/invoice status.","tags":["Checkout"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"paymentIntentId":{"type":"string","description":"Stripe PaymentIntent ID to verify"},"labId":{"type":"string","description":"Lab ID the payment was made to"}},"required":["paymentIntentId","labId"]}}}},"responses":{"200":{"description":"Payment verified successfully with amount and currency details","content":{"application/json":{"schema":{"type":"object","properties":{"verified":{"type":"boolean"},"paymentIntentId":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"convertedOrderId":{"type":"string"}},"required":["verified","paymentIntentId","amount","currency","convertedOrderId"]}}}},"400":{"description":"Payment not completed, missing parameters, or lab not connected to Stripe"},"500":{"description":"Stripe secret key not configured"}}}},"/checkout/cancel-order-payment":{"post":{"operationId":"cancelCheckoutOrderPayment","description":"Cancel or reconcile the authenticated clinic's active upfront order payment before unlocking its prepared draft.","tags":["Checkout"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1},"draftOrderId":{"type":"string","minLength":1}},"required":["labId","draftOrderId"]}}}},"responses":{"200":{"description":"Payment canceled or reconciled","content":{"application/json":{"schema":{"type":"object","properties":{"unlocked":{"type":"boolean"},"convertedOrderId":{"type":["string","null"]},"paymentIntentId":{"type":["string","null"]}},"required":["unlocked","convertedOrderId","paymentIntentId"]}}}},"409":{"description":"Payment is already final or cannot be canceled safely"},"500":{"description":"Stripe secret key not configured"}}}},"/clinic-payment-methods":{"get":{"operationId":"listClinicPaymentMethods","description":"List saved payment methods for the authenticated clinic with a specific lab. Clinic-owner only. Requires labId query parameter to identify the lab's Stripe Connect account.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Lab ID to retrieve payment methods for","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Lab ID to retrieve payment methods for","name":"labId","in":"query"}],"responses":{"200":{"description":"List of saved payment methods with card details and default status","content":{"application/json":{"schema":{"type":"object","properties":{"paymentMethods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"brand":{"type":["string","null"]},"last4":{"type":["string","null"]},"expMonth":{"type":["number","null"]},"expYear":{"type":["number","null"]},"isDefault":{"type":"boolean"}},"required":["id","type","brand","last4","expMonth","expYear","isDefault"]}}},"required":["paymentMethods"]}}}},"400":{"description":"Missing labId parameter","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal server error"}}}},"/clinic-payment-methods/{id}":{"put":{"operationId":"setDefaultClinicPaymentMethod","description":"Set a saved payment method as the default for the authenticated clinic with a specific lab. Clinic-owner only.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Stripe payment method ID","example":"pm_1234567890abcdef"},"required":true,"description":"Stripe payment method ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1,"description":"Lab ID the payment method belongs to"}},"required":["labId"]}}}},"responses":{"200":{"description":"Payment method set as default successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Missing labId or lab has not connected Stripe","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No Stripe customer mapping found for this clinic-lab pair","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteClinicPaymentMethod","description":"Detach and remove a saved payment method from the authenticated clinic's Stripe customer. Clinic-owner only. Requires labId query parameter.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Stripe payment method ID to detach","example":"pm_1234567890abcdef"},"required":true,"description":"Stripe payment method ID to detach","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Lab ID the payment method belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Lab ID the payment method belongs to","name":"labId","in":"query"}],"responses":{"200":{"description":"Payment method detached successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Missing labId or lab has not connected Stripe","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Customer, partnership, or payment method not found"},"409":{"description":"Payment method is used by active autopay or the Stripe connection changed"},"500":{"description":"Internal server error"}}}},"/clinic-payment-methods/setup-intent":{"post":{"operationId":"createClinicSetupIntent","description":"Create a Stripe SetupIntent to save a new payment method for the authenticated clinic with a specific lab. Clinic-owner only. Automatically creates a Stripe customer if one does not exist.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"labId":{"type":"string","minLength":1,"description":"Lab ID to create the setup intent for"}},"required":["labId"]}}}},"responses":{"200":{"description":"Setup intent created with client secret for Stripe.js confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"clientSecret":{"type":["string","null"]},"stripeAccountId":{"type":"string"}},"required":["clientSecret","stripeAccountId"]}}}},"400":{"description":"Missing labId or lab has not connected Stripe","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal server error"}}}},"/clinic-payment-methods/autopay/{partnershipId}":{"get":{"operationId":"getClinicAutopayMandate","summary":"Get clinic autopay consent","description":"Returns the clinic-owned saved-card autopay state for a partnership. Both partnership parties may read it; labs cannot mutate it.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"partnershipId","in":"path"}],"responses":{"200":{"description":"Autopay mandate state","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"status":{"type":["string","null"],"enum":["active","suspended","revoked",null]},"consentedAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"suspendedAt":{"type":["string","null"],"format":"date-time"}},"required":["enabled","status","consentedAt","revokedAt","suspendedAt"]}}}},"404":{"description":"Partnership not found"}}},"delete":{"operationId":"revokeClinicAutopayMandate","summary":"Revoke clinic-owned autopay","description":"Revokes future off-session charges for this partnership. Only a clinic owner can perform this operation; labs have read-only visibility.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"partnershipId","in":"path"}],"responses":{"200":{"description":"Autopay revoked","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"status":{"type":["string","null"],"enum":["active","suspended","revoked",null]},"consentedAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"suspendedAt":{"type":["string","null"],"format":"date-time"}},"required":["enabled","status","consentedAt","revokedAt","suspendedAt"]}}}},"404":{"description":"Partnership not found"}}}},"/clinic-payment-methods/autopay":{"post":{"operationId":"enableClinicAutopayMandate","summary":"Enable clinic-owned saved-card autopay","description":"Lets a clinic owner consent to off-session card charges for one active lab partnership. The selected card must already belong to that lab's connected Stripe customer.","tags":["Clinic Payment Methods"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"partnershipId":{"type":"string","minLength":1},"paymentMethodId":{"type":"string","minLength":1}},"required":["partnershipId","paymentMethodId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Autopay enabled","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"status":{"type":["string","null"],"enum":["active","suspended","revoked",null]},"consentedAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"suspendedAt":{"type":["string","null"],"format":"date-time"}},"required":["enabled","status","consentedAt","revokedAt","suspendedAt"]}}}},"400":{"description":"Stripe is unavailable for this partnership"},"404":{"description":"Partnership, customer, or card not found"},"409":{"description":"The lab's connected account changed"},"500":{"description":"Stripe is not configured"}}}},"/dashboard/stats":{"get":{"tags":["Dashboard"],"operationId":"getLabDashboardStats","summary":"Get lab dashboard statistics","description":"Returns monthly order counts, revenue totals, completion rates, and deadline breakdown for the authenticated lab. Compares current month to previous month for trend calculation.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Monthly stats, trends, and deadline counts","content":{"application/json":{"schema":{"type":"object","properties":{"monthly":{"type":"object","properties":{"totalOrders":{"type":"number"},"completedOrders":{"type":"number"},"pendingOrders":{"type":"number"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"description":"Currency scope of the legacy monetary fields; order counts include every currency."},"revenue":{"type":"number","description":"Compatibility projection of revenueExact for the lab currency."},"revenueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"avgOrderValue":{"type":"number","description":"Compatibility projection of avgOrderValueExact for the lab currency."},"avgOrderValueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"currencyTotals":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"totalOrders":{"type":"number"},"completedOrders":{"type":"number"},"pendingOrders":{"type":"number"},"revenueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"previousRevenueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"avgOrderValueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"trends":{"type":"object","properties":{"orders":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"completed":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"revenue":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"pending":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]}},"required":["orders","completed","revenue","pending"]}},"required":["currency","totalOrders","completedOrders","pendingOrders","revenueExact","previousRevenueExact","avgOrderValueExact","trends"]}},"trends":{"type":"object","properties":{"orders":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"completed":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"revenue":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"pending":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]}},"required":["orders","completed","revenue","pending"]}},"required":["totalOrders","completedOrders","pendingOrders","currency","revenue","revenueExact","avgOrderValue","avgOrderValueExact","currencyTotals","trends"]},"deadlines":{"type":"object","properties":{"overdue":{"type":"number"},"today":{"type":"number"},"tomorrow":{"type":"number"},"thisWeek":{"type":"number"}},"required":["overdue","today","tomorrow","thisWeek"]}},"required":["monthly","deadlines"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/dashboard/today":{"get":{"tags":["Dashboard"],"operationId":"getLabDashboardToday","summary":"Get today's hourly breakdown","description":"Returns hourly order or revenue data for a given day (defaults to today). Includes cumulative totals and end-of-day projection. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Hourly data points with cumulative totals and projection","content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string"},"metric":{"type":"string","enum":["orders","revenue"]},"currency":{"type":["string","null"],"enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR",null],"description":"Currency of the numeric compatibility projection; null for the orders metric."},"dataPoints":{"type":"array","items":{"type":"object","properties":{"hour":{"type":"number"},"value":{"type":"number"},"cumulative":{"type":"number"},"valueExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"cumulativeExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["hour","value","cumulative","valueExact","cumulativeExact"]}},"currencySeries":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"dataPoints":{"type":"array","items":{"type":"object","properties":{"hour":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"cumulativeExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["hour","valueExact","cumulativeExact"]}},"summary":{"type":"object","properties":{"currentValueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"currentTime":{"type":"string"},"projectedEndOfDayExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["currentValueExact","currentTime","projectedEndOfDayExact"]}},"required":["currency","dataPoints","summary"]}},"summary":{"type":"object","properties":{"currentValue":{"type":"number"},"currentValueExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"currentTime":{"type":"string"},"projectedEndOfDay":{"type":"number"},"projectedEndOfDayExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["currentValue","currentValueExact","currentTime","projectedEndOfDay","projectedEndOfDayExact"]}},"required":["date","metric","currency","dataPoints","currencySeries","summary"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/dashboard/trends":{"get":{"tags":["Dashboard"],"operationId":"getLabDashboardTrends","summary":"Get order and revenue trends","description":"Returns daily submitted and completed order counts and revenue over a date range (defaults to last 30 days). Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Daily series for submitted/completed orders with totals","content":{"application/json":{"schema":{"type":"object","properties":{"dateRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"description":"Currency scope of the legacy monetary fields; order counts include every currency."},"series":{"type":"object","properties":{"submitted":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"value":{"type":"number","description":"Compatibility projection of valueExact for the lab currency."},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","value","valueExact"]}},"completed":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"value":{"type":"number","description":"Compatibility projection of valueExact for the lab currency."},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","value","valueExact"]}}},"required":["submitted","completed"]},"currencySeries":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"series":{"type":"object","properties":{"submitted":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","valueExact"]}},"completed":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","valueExact"]}}},"required":["submitted","completed"]},"totals":{"type":"object","properties":{"submitted":{"type":"object","properties":{"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","valueExact"]},"completed":{"type":"object","properties":{"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","valueExact"]}},"required":["submitted","completed"]}},"required":["currency","series","totals"]}},"totals":{"type":"object","properties":{"submitted":{"type":"object","properties":{"count":{"type":"number"},"value":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","value","valueExact"]},"completed":{"type":"object","properties":{"count":{"type":"number"},"value":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","value","valueExact"]}},"required":["submitted","completed"]}},"required":["dateRange","currency","series","currencySeries","totals"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/dashboard/operations":{"get":{"tags":["Dashboard"],"operationId":"getLabDashboardOperations","summary":"Get active orders by deadline","description":"Lists active orders filtered by deadline urgency (overdue, today, tomorrow, or all). Returns paginated results with patient, clinic, and category info. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["overdue","today","tomorrow","all"],"default":"all","example":"all"},"required":false,"name":"deadline","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"submitted"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Paginated list of active orders with deadline info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"patient":{"type":["object","null"],"properties":{"firstName":{"type":"string"},"lastName":{"type":"string"}},"required":["firstName","lastName"]},"status":{"type":"string"},"practice":{"type":["object","null"],"properties":{"name":{"type":"string"}},"required":["name"]},"doctor":{"type":["string","null"]},"category":{"type":["string","null"]},"dueDate":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","orderNumber","patient","status","practice","doctor","category","dueDate","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/dashboard/blocked-orders":{"get":{"tags":["Dashboard"],"operationId":"getLabDashboardBlockedOrders","summary":"List orders currently blocked by an active hold","description":"Returns a bounded cursor page of on-hold orders for the authenticated lab. Patient names honor the order's lab-masking policy.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["critical","high","medium"]},"required":false,"name":"severity","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":1024},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":10},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Blocked orders ordered by newest active hold","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"holdId":{"type":"string"},"holdReasonName":{"type":"string"},"blockedAt":{"type":"string"},"blockedHours":{"type":"integer","minimum":0},"overdueDays":{"type":"integer","minimum":0},"severity":{"type":"string","enum":["critical","high","medium"]},"deadline":{"type":["string","null"]},"patientName":{"type":["string","null"]},"practiceName":{"type":["string","null"]},"deepLink":{"type":"string"}},"required":["id","orderNumber","holdId","holdReasonName","blockedAt","blockedHours","overdueDays","severity","deadline","patientName","practiceName","deepLink"]}},"counts":{"type":"object","properties":{"all":{"type":"integer","minimum":0},"critical":{"type":"integer","minimum":0},"high":{"type":"integer","minimum":0},"medium":{"type":"integer","minimum":0}},"required":["all","critical","high","medium"]},"nextCursor":{"type":["string","null"]}},"required":["data","counts","nextCursor"]}}}},"400":{"description":"Invalid cursor or query"},"401":{"description":"Unauthorized"},"403":{"description":"Only labs can list blocked orders"}}}},"/dashboard/clinic/stats":{"get":{"tags":["Dashboard"],"operationId":"getClinicDashboardStats","summary":"Get exact clinic dashboard counters","description":"Returns tenant-scoped aggregate counters without loading or returning patient rows.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Clinic dashboard counters","content":{"application/json":{"schema":{"type":"object","properties":{"actionsRequired":{"type":"integer","minimum":0},"awaitingApproval":{"type":"integer","minimum":0},"activeOrders":{"type":"integer","minimum":0},"deliveriesToday":{"type":"integer","minimum":0}},"required":["actionsRequired","awaitingApproval","activeOrders","deliveriesToday"]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Only clinics can read clinic dashboard stats"}}}},"/dashboard/clinic/actions":{"get":{"tags":["Dashboard"],"operationId":"getClinicDashboardActions","summary":"List clinic actions","description":"Returns a bounded, cursor-paginated union of current clinic actions. Deep links are derived from trusted source rows.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2048},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":6},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Clinic action page","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"sourceKind":{"type":"string"},"sourceId":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":"string"},"actionType":{"type":"string","enum":["approve_surgical_report","upload_phase_files"]},"priority":{"type":"string","enum":["high","medium","low"]},"dueAt":{"type":["string","null"]},"createdAt":{"type":"string"},"deepLink":{"type":"string"}},"required":["sourceKind","sourceId","orderId","orderNumber","actionType","priority","dueAt","createdAt","deepLink"]}},"nextCursor":{"type":["string","null"]}},"required":["data","nextCursor"]}}}},"400":{"description":"Invalid cursor or query"},"401":{"description":"Unauthorized"},"403":{"description":"Only clinics can list clinic actions"}}}},"/dashboard/clinic/activity":{"get":{"tags":["Dashboard"],"operationId":"getClinicDashboardActivity","summary":"List clinic-safe recent order activity","description":"Returns only safe event classifications and order references. Raw activity payloads, notes, attachment IDs, and lab-internal task events are never selected.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":1024},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":20,"default":4},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Clinic activity page","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"orderNumber":{"type":"string"},"eventType":{"type":"string","enum":["status_changed","phase_changed","file_uploaded","task_review_event"]},"summaryKey":{"type":"string"},"createdAt":{"type":"string"},"deepLink":{"type":"string"}},"required":["id","orderId","orderNumber","eventType","summaryKey","createdAt","deepLink"]}},"nextCursor":{"type":["string","null"]}},"required":["data","nextCursor"]}}}},"400":{"description":"Invalid cursor or query"},"401":{"description":"Unauthorized"},"403":{"description":"Only clinics can list clinic activity"}}}},"/dashboard/overview":{"get":{"tags":["Dashboard"],"operationId":"getLabDashboardOverview","summary":"Bootstrap all lab dashboard panels in one call","description":"Aggregates /dashboard/stats, /dashboard/today, /dashboard/trends, and /dashboard/operations into a single parallel request. Defaults: today's date, revenue metric, 30-day trends, all deadlines, page 1, limit 10.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Combined stats, today, trends, and operations payload","content":{"application/json":{"schema":{"type":"object","properties":{"stats":{"type":"object","properties":{"monthly":{"type":"object","properties":{"totalOrders":{"type":"number"},"completedOrders":{"type":"number"},"pendingOrders":{"type":"number"},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"description":"Currency scope of the legacy monetary fields; order counts include every currency."},"revenue":{"type":"number","description":"Compatibility projection of revenueExact for the lab currency."},"revenueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"avgOrderValue":{"type":"number","description":"Compatibility projection of avgOrderValueExact for the lab currency."},"avgOrderValueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"currencyTotals":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"totalOrders":{"type":"number"},"completedOrders":{"type":"number"},"pendingOrders":{"type":"number"},"revenueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"previousRevenueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"avgOrderValueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"trends":{"type":"object","properties":{"orders":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"completed":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"revenue":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"pending":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]}},"required":["orders","completed","revenue","pending"]}},"required":["currency","totalOrders","completedOrders","pendingOrders","revenueExact","previousRevenueExact","avgOrderValueExact","trends"]}},"trends":{"type":"object","properties":{"orders":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"completed":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"revenue":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]},"pending":{"type":"object","properties":{"change":{"type":"string"},"type":{"type":"string","enum":["increase","decrease","neutral"]}},"required":["change","type"]}},"required":["orders","completed","revenue","pending"]}},"required":["totalOrders","completedOrders","pendingOrders","currency","revenue","revenueExact","avgOrderValue","avgOrderValueExact","currencyTotals","trends"]},"deadlines":{"type":"object","properties":{"overdue":{"type":"number"},"today":{"type":"number"},"tomorrow":{"type":"number"},"thisWeek":{"type":"number"}},"required":["overdue","today","tomorrow","thisWeek"]}},"required":["monthly","deadlines"]},"today":{"type":"object","properties":{"date":{"type":"string"},"metric":{"type":"string","enum":["orders","revenue"]},"currency":{"type":["string","null"],"enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR",null],"description":"Currency of the numeric compatibility projection; null for the orders metric."},"dataPoints":{"type":"array","items":{"type":"object","properties":{"hour":{"type":"number"},"value":{"type":"number"},"cumulative":{"type":"number"},"valueExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"cumulativeExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["hour","value","cumulative","valueExact","cumulativeExact"]}},"currencySeries":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"dataPoints":{"type":"array","items":{"type":"object","properties":{"hour":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"cumulativeExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["hour","valueExact","cumulativeExact"]}},"summary":{"type":"object","properties":{"currentValueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"currentTime":{"type":"string"},"projectedEndOfDayExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["currentValueExact","currentTime","projectedEndOfDayExact"]}},"required":["currency","dataPoints","summary"]}},"summary":{"type":"object","properties":{"currentValue":{"type":"number"},"currentValueExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"},"currentTime":{"type":"string"},"projectedEndOfDay":{"type":"number"},"projectedEndOfDayExact":{"type":["string","null"],"pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["currentValue","currentValueExact","currentTime","projectedEndOfDay","projectedEndOfDayExact"]}},"required":["date","metric","currency","dataPoints","currencySeries","summary"]},"trends":{"type":"object","properties":{"dateRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]},"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"],"description":"Currency scope of the legacy monetary fields; order counts include every currency."},"series":{"type":"object","properties":{"submitted":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"value":{"type":"number","description":"Compatibility projection of valueExact for the lab currency."},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","value","valueExact"]}},"completed":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"value":{"type":"number","description":"Compatibility projection of valueExact for the lab currency."},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","value","valueExact"]}}},"required":["submitted","completed"]},"currencySeries":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["GBP","EUR","USD","CAD","AUD","NZD","CHF","SEK","NOK","DKK","PLN","CZK","HUF","RON","INR","JPY","CNY","SGD","HKD","MXN","BRL","ZAR"]},"series":{"type":"object","properties":{"submitted":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","valueExact"]}},"completed":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["date","count","valueExact"]}}},"required":["submitted","completed"]},"totals":{"type":"object","properties":{"submitted":{"type":"object","properties":{"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","valueExact"]},"completed":{"type":"object","properties":{"count":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","valueExact"]}},"required":["submitted","completed"]}},"required":["currency","series","totals"]}},"totals":{"type":"object","properties":{"submitted":{"type":"object","properties":{"count":{"type":"number"},"value":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","value","valueExact"]},"completed":{"type":"object","properties":{"count":{"type":"number"},"value":{"type":"number"},"valueExact":{"type":"string","pattern":"^-?\\d+(?:\\.\\d+)?$/u","description":"Exact decimal amount in major currency units. This string is authoritative; numeric money fields are compatibility projections.","example":"1234.56"}},"required":["count","value","valueExact"]}},"required":["submitted","completed"]}},"required":["dateRange","currency","series","currencySeries","totals"]},"operations":{}},"required":["stats","today","trends"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/calendar/time-off":{"get":{"tags":["Calendar"],"operationId":"listCalendarTimeOff","summary":"List time-off entries","description":"Returns paginated time-off and bank holiday entries for the authenticated lab. Optionally includes inactive entries. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"number","minimum":1,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated list of time-off entries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean"},"isBankHoliday":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","startDate","endDate","isRecurringAnnually","isBankHoliday","isActive","sortOrder","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"}}},"post":{"tags":["Calendar"],"operationId":"createCalendarTimeOff","summary":"Create a time-off entry","description":"Creates a new time-off or bank holiday entry for the authenticated lab. Validates that the start date is before or equal to the end date. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean","default":false},"isBankHoliday":{"type":"boolean","default":false},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["name","startDate","endDate"]}}}},"responses":{"201":{"description":"Time-off entry created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"entry":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean"},"isBankHoliday":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","startDate","endDate","isRecurringAnnually","isBankHoliday","isActive","sortOrder","createdAt","updatedAt"]}},"required":["entry"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/calendar/time-off/{id}":{"get":{"tags":["Calendar"],"operationId":"getCalendarTimeOff","summary":"Get a time-off entry by ID","description":"Returns a single time-off entry by ID, scoped to the authenticated lab. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Time-off entry ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Time-off entry ID","name":"id","in":"path"}],"responses":{"200":{"description":"Time-off entry details","content":{"application/json":{"schema":{"type":"object","properties":{"entry":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean"},"isBankHoliday":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","startDate","endDate","isRecurringAnnually","isBankHoliday","isActive","sortOrder","createdAt","updatedAt"]}},"required":["entry"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Time-off entry not found"}}},"put":{"tags":["Calendar"],"operationId":"updateCalendarTimeOff","summary":"Update a time-off entry","description":"Updates an existing time-off entry by ID. Validates date ordering. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Time-off entry ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Time-off entry ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean"},"isBankHoliday":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"Time-off entry updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"entry":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean"},"isBankHoliday":{"type":"boolean"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","startDate","endDate","isRecurringAnnually","isBankHoliday","isActive","sortOrder","createdAt","updatedAt"]}},"required":["entry"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Time-off entry not found"}}},"delete":{"tags":["Calendar"],"operationId":"deleteCalendarTimeOff","summary":"Deactivate a time-off entry","description":"Soft-deletes a time-off entry by setting it to inactive. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Time-off entry ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Time-off entry ID","name":"id","in":"path"}],"responses":{"200":{"description":"Time-off entry deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"entry":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","isActive","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["entry","message"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Time-off entry not found"}}}},"/calendar/time-off/seed-holidays":{"post":{"tags":["Calendar"],"operationId":"seedCalendarHolidays","summary":"Seed UK bank holidays","description":"Populates the lab's calendar with default UK bank holidays. Only seeds if the lab has no existing calendar entries. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"201":{"description":"Bank holidays seeded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"seeded":{"type":"boolean"},"count":{"type":"number"}},"required":["message","seeded","count"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/calendar/working-days":{"get":{"tags":["Calendar"],"operationId":"getWorkingDays","summary":"Get organization working days","description":"Returns the persisted working calendar for the authenticated organization or an explicitly selected active partner lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":false,"name":"labId","in":"query"}],"responses":{"200":{"description":"Array of working day numbers","content":{"application/json":{"schema":{"type":"object","properties":{"workingDays":{"type":"array","items":{"type":"integer","minimum":0,"maximum":6},"minItems":1},"timezone":{"type":"string"},"closures":{"type":"array","items":{"type":"object","properties":{"startDate":{"type":"string"},"endDate":{"type":"string"},"isRecurringAnnually":{"type":"boolean"}},"required":["startDate","endDate","isRecurringAnnually"]}}},"required":["workingDays","timezone","closures"]}}}},"400":{"description":"Requested organization is not a lab"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"The requested lab is not an active partner"},"404":{"description":"Organization not found"}}},"put":{"tags":["Calendar"],"operationId":"updateWorkingDays","summary":"Update organization working days","description":"Sets which days of the week (0=Sunday through 6=Saturday) the lab operates. At least one day must be selected. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"workingDays":{"type":"array","items":{"type":"integer","minimum":0,"maximum":6},"minItems":1}},"required":["workingDays"]}}}},"responses":{"200":{"description":"Working days updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"workingDays":{"type":"array","items":{"type":"number"}}},"required":["workingDays"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/reports":{"get":{"tags":["Reports"],"operationId":"listReports","summary":"List reports","description":"Returns the persisted built-in and custom report definitions for the authenticated lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["built_in","custom"]},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"List of report definitions","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":"string"},"type":{"type":"string","enum":["built_in","custom"]},"templateSlug":{"type":["string","null"]},"isPinned":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"}},"required":["id","name","description","icon","type","templateSlug","isPinned","sortOrder","createdAt"]}}}}},"401":{"description":"Unauthorized — valid session required"}}},"post":{"tags":["Reports"],"operationId":"createReport","summary":"Create a custom report","description":"Creates a custom report after validating every versioned field and operator against its code-owned data-source adapter. Lab owner/admin only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":500},"icon":{"type":"string","maxLength":50},"config":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"],"default":"v1"},"dataSource":{"type":"string","enum":["orders","order_items","clients","products"]},"columns":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string","minLength":1,"maxLength":100},"aggregation":{"type":"string","enum":["sum","count","avg","min","max"]},"format":{"type":"string","minLength":1,"maxLength":32},"width":{"type":"number","exclusiveMinimum":0,"maximum":2000}},"required":["fieldKey"],"additionalProperties":false},"minItems":1,"maxItems":20},"filters":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"operator":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]},"value":{}},"required":["fieldKey","operator"],"additionalProperties":false},"maxItems":20,"default":[]},"groupBy":{"type":"array","items":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"maxItems":10,"default":[]},"sortBy":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["fieldKey","direction"],"additionalProperties":false},"maxItems":5,"default":[]},"dateRange":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"preset":{"type":"string","enum":["today","yesterday","this_week","last_week","this_month","last_month","last_30_days","last_90_days","this_quarter","last_quarter","this_year","last_year","all_time"]},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["fieldKey"],"additionalProperties":false},"chart":{"type":"object","properties":{"type":{"type":"string","enum":["bar","line","area","pie","donut"]},"xAxisField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"yAxisFields":{"type":"array","items":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"minItems":1,"maxItems":5},"enabled":{"type":"boolean"}},"required":["type","xAxisField","yAxisFields","enabled"],"additionalProperties":false},"pageSize":{"type":"integer","minimum":1,"maximum":500}},"required":["dataSource","columns"],"additionalProperties":false},"isPinned":{"type":"boolean"}},"required":["name","config"],"additionalProperties":false}}}},"responses":{"201":{"description":"Custom report created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":"string"},"type":{"type":"string","enum":["built_in","custom"]},"templateSlug":{"type":["string","null"]},"isPinned":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"}},"required":["id","name","description","icon","type","templateSlug","isPinned","sortOrder","createdAt"]}}}},"400":{"description":"Invalid report configuration"},"401":{"description":"Unauthorized — valid session required"}}}},"/reports/data-sources":{"get":{"tags":["Reports"],"operationId":"listReportDataSources","summary":"List available report data sources","description":"Returns the serializable field, type, aggregation, sort, and filter allowlists for all four code-owned report adapters.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Versioned data-source definitions","content":{"application/json":{"schema":{"type":"object","properties":{"orders":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"]},"key":{"type":"string","enum":["orders","order_items","clients","products"]},"label":{"type":"string"},"description":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"type":"string","enum":["string","number","date","boolean","currency"]},"groupable":{"type":"boolean"},"sortable":{"type":"boolean"},"aggregations":{"type":"array","items":{"type":"string","enum":["sum","count","avg","min","max"]}},"filterOperators":{"type":"array","items":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]}},"enumValues":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"description":{"type":"string"}},"required":["key","label","type","groupable","sortable","filterOperators"],"additionalProperties":false}},"defaultDateField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"}},"required":["definitionVersion","key","label","description","fields","defaultDateField"],"additionalProperties":false},"order_items":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"]},"key":{"type":"string","enum":["orders","order_items","clients","products"]},"label":{"type":"string"},"description":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"type":"string","enum":["string","number","date","boolean","currency"]},"groupable":{"type":"boolean"},"sortable":{"type":"boolean"},"aggregations":{"type":"array","items":{"type":"string","enum":["sum","count","avg","min","max"]}},"filterOperators":{"type":"array","items":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]}},"enumValues":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"description":{"type":"string"}},"required":["key","label","type","groupable","sortable","filterOperators"],"additionalProperties":false}},"defaultDateField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"}},"required":["definitionVersion","key","label","description","fields","defaultDateField"],"additionalProperties":false},"clients":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"]},"key":{"type":"string","enum":["orders","order_items","clients","products"]},"label":{"type":"string"},"description":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"type":"string","enum":["string","number","date","boolean","currency"]},"groupable":{"type":"boolean"},"sortable":{"type":"boolean"},"aggregations":{"type":"array","items":{"type":"string","enum":["sum","count","avg","min","max"]}},"filterOperators":{"type":"array","items":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]}},"enumValues":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"description":{"type":"string"}},"required":["key","label","type","groupable","sortable","filterOperators"],"additionalProperties":false}},"defaultDateField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"}},"required":["definitionVersion","key","label","description","fields","defaultDateField"],"additionalProperties":false},"products":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"]},"key":{"type":"string","enum":["orders","order_items","clients","products"]},"label":{"type":"string"},"description":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"type":"string","enum":["string","number","date","boolean","currency"]},"groupable":{"type":"boolean"},"sortable":{"type":"boolean"},"aggregations":{"type":"array","items":{"type":"string","enum":["sum","count","avg","min","max"]}},"filterOperators":{"type":"array","items":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]}},"enumValues":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"description":{"type":"string"}},"required":["key","label","type","groupable","sortable","filterOperators"],"additionalProperties":false}},"defaultDateField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"}},"required":["definitionVersion","key","label","description","fields","defaultDateField"],"additionalProperties":false}},"required":["orders","order_items","clients","products"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/reports/{id}":{"get":{"tags":["Reports"],"operationId":"getReport","summary":"Get a report by ID","description":"Returns one tenant-scoped report. Built-in slugs are expanded into their explicit adapter-backed configuration.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Report details with executable configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":"string"},"type":{"type":"string","enum":["built_in","custom"]},"templateSlug":{"type":["string","null"]},"isPinned":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"config":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"],"default":"v1"},"dataSource":{"type":"string","enum":["orders","order_items","clients","products"]},"columns":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string","minLength":1,"maxLength":100},"aggregation":{"type":"string","enum":["sum","count","avg","min","max"]},"format":{"type":"string","minLength":1,"maxLength":32},"width":{"type":"number","exclusiveMinimum":0,"maximum":2000}},"required":["fieldKey"],"additionalProperties":false},"minItems":1,"maxItems":20},"filters":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"operator":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]},"value":{}},"required":["fieldKey","operator"],"additionalProperties":false},"maxItems":20,"default":[]},"groupBy":{"type":"array","items":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"maxItems":10,"default":[]},"sortBy":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["fieldKey","direction"],"additionalProperties":false},"maxItems":5,"default":[]},"dateRange":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"preset":{"type":"string","enum":["today","yesterday","this_week","last_week","this_month","last_month","last_30_days","last_90_days","this_quarter","last_quarter","this_year","last_year","all_time"]},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["fieldKey"],"additionalProperties":false},"chart":{"type":"object","properties":{"type":{"type":"string","enum":["bar","line","area","pie","donut"]},"xAxisField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"yAxisFields":{"type":"array","items":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"minItems":1,"maxItems":5},"enabled":{"type":"boolean"}},"required":["type","xAxisField","yAxisFields","enabled"],"additionalProperties":false},"pageSize":{"type":"integer","minimum":1,"maximum":500}},"required":["dataSource","columns"],"additionalProperties":false},"createdBy":{"type":["string","null"]},"updatedAt":{"type":"string"}},"required":["id","name","description","icon","type","templateSlug","isPinned","sortOrder","createdAt","config","createdBy","updatedAt"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Report not found"}}},"patch":{"tags":["Reports"],"operationId":"updateReport","summary":"Update a report","description":"Updates a tenant-scoped report. Built-in query definitions cannot be replaced. Lab owner/admin only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":500},"icon":{"type":"string","maxLength":50},"config":{"type":"object","properties":{"definitionVersion":{"type":"string","enum":["v1"],"default":"v1"},"dataSource":{"type":"string","enum":["orders","order_items","clients","products"]},"columns":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string","minLength":1,"maxLength":100},"aggregation":{"type":"string","enum":["sum","count","avg","min","max"]},"format":{"type":"string","minLength":1,"maxLength":32},"width":{"type":"number","exclusiveMinimum":0,"maximum":2000}},"required":["fieldKey"],"additionalProperties":false},"minItems":1,"maxItems":20},"filters":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"operator":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]},"value":{}},"required":["fieldKey","operator"],"additionalProperties":false},"maxItems":20,"default":[]},"groupBy":{"type":"array","items":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"maxItems":10,"default":[]},"sortBy":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["fieldKey","direction"],"additionalProperties":false},"maxItems":5,"default":[]},"dateRange":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"preset":{"type":"string","enum":["today","yesterday","this_week","last_week","this_month","last_month","last_30_days","last_90_days","this_quarter","last_quarter","this_year","last_year","all_time"]},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["fieldKey"],"additionalProperties":false},"chart":{"type":"object","properties":{"type":{"type":"string","enum":["bar","line","area","pie","donut"]},"xAxisField":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"yAxisFields":{"type":"array","items":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"minItems":1,"maxItems":5},"enabled":{"type":"boolean"}},"required":["type","xAxisField","yAxisFields","enabled"],"additionalProperties":false},"pageSize":{"type":"integer","minimum":1,"maximum":500}},"required":["dataSource","columns"],"additionalProperties":false},"isPinned":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0,"maximum":1000000}},"additionalProperties":false}}}},"responses":{"200":{"description":"Report updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid report configuration"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Built-in query definitions are immutable"},"404":{"description":"Report not found"}}},"delete":{"tags":["Reports"],"operationId":"deleteReport","summary":"Delete a custom report","description":"Permanently deletes a custom report. Built-in reports cannot be deleted. Lab owner/admin only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Report deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Cannot delete built-in reports"},"404":{"description":"Report not found"}}}},"/reports/{id}/execute":{"post":{"tags":["Reports"],"operationId":"executeReport","summary":"Execute a report query","description":"Runs one adapter-backed, tenant-scoped report query with bounded fields, filters, sorts, pagination, and database time.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"page":{"type":"integer","minimum":1,"maximum":1000000,"default":1},"limit":{"type":"integer","minimum":1,"maximum":500,"default":50},"dateRange":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"preset":{"type":"string","enum":["today","yesterday","this_week","last_week","this_month","last_month","last_30_days","last_90_days","this_quarter","last_quarter","this_year","last_year","all_time"]},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["fieldKey"],"additionalProperties":false},"sortBy":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["fieldKey","direction"],"additionalProperties":false},"maxItems":5},"additionalFilters":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"operator":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]},"value":{}},"required":["fieldKey","operator"],"additionalProperties":false},"maxItems":20}},"additionalProperties":false}}}},"responses":{"200":{"description":"Paginated report execution results","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","additionalProperties":{}}},"columns":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"type":"string","enum":["string","number","date","boolean","currency"]},"format":{"type":"string"},"aggregation":{"type":"string","enum":["sum","count","avg","min","max"]}},"required":["key","label","type"],"additionalProperties":false}},"pagination":{"type":"object","properties":{"page":{"type":"integer","exclusiveMinimum":0},"limit":{"type":"integer","exclusiveMinimum":0},"total":{"type":"integer","minimum":0},"totalPages":{"type":"integer","minimum":0}},"required":["page","limit","total","totalPages"]}},"required":["data","columns","pagination"]}}}},"400":{"description":"Invalid report configuration"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Report not found"},"408":{"description":"Report query exceeded its time limit"}}}},"/reports/{id}/export":{"post":{"tags":["Reports"],"operationId":"exportReport","summary":"Export a report as CSV","description":"Executes one tenant-scoped report query and returns a synchronous CSV under fixed request, row, byte, time, and query-fan-out ceilings.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dateRange":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"preset":{"type":"string","enum":["today","yesterday","this_week","last_week","this_month","last_month","last_30_days","last_90_days","this_quarter","last_quarter","this_year","last_year","all_time"]},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["fieldKey"],"additionalProperties":false},"sortBy":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["fieldKey","direction"],"additionalProperties":false},"maxItems":5},"additionalFilters":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","minLength":4,"maxLength":64,"pattern":"^v1\\.[a-z][a-z0-9_]*$"},"operator":{"type":"string","enum":["eq","neq","gt","gte","lt","lte","contains","not_contains","in","not_in","between","is_null","is_not_null"]},"value":{}},"required":["fieldKey","operator"],"additionalProperties":false},"maxItems":20},"format":{"type":"string","enum":["csv"],"default":"csv"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Bounded CSV file download","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Invalid report configuration"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Report not found"},"408":{"description":"Report export exceeded its time limit"},"413":{"description":"Report export exceeded its row or byte limit"},"429":{"description":"Report export request budget exceeded"}}}},"/export/balance-list":{"post":{"tags":["Export"],"operationId":"exportBalanceList","summary":"Export balance list as CSV","description":"Exports invoice/balance data for the lab's clients as a CSV file. Caller specifies which fields to include. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":6}},"required":["fields"]}}}},"responses":{"200":{"description":"CSV file download with balance list data","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized — valid session required"},"429":{"description":"CSV export budget exceeded"}}}},"/export/clients":{"post":{"tags":["Export"],"operationId":"exportClients","summary":"Export clients as CSV","description":"Exports partner clinic data including contact info, price list, and practice group as a CSV file. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":10}},"required":["fields"]}}}},"responses":{"200":{"description":"CSV file download with client data","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized — valid session required"},"429":{"description":"CSV export budget exceeded"}}}},"/export/price-lists":{"post":{"tags":["Export"],"operationId":"exportPriceLists","summary":"Export price lists as CSV","description":"Exports versioned product pricing rules with their stable book and publication metadata. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":10}},"required":["fields"]}}}},"responses":{"200":{"description":"CSV file download with price list data","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized — valid session required"},"413":{"description":"CSV export lookup limit exceeded"},"429":{"description":"CSV export budget exceeded"}}}},"/export/products":{"post":{"tags":["Export"],"operationId":"exportProducts","summary":"Export products as CSV","description":"Exports the lab's product catalog including categories, subcategories, standards, and pricing as a CSV file. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":13}},"required":["fields"]}}}},"responses":{"200":{"description":"CSV file download with product data","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized — valid session required"},"413":{"description":"CSV export lookup limit exceeded"},"429":{"description":"CSV export budget exceeded"}}}},"/import/balance-list":{"post":{"tags":["Import"],"operationId":"importBalanceList","summary":"Import balance list from CSV data","description":"Imports non-negative opening balances as new invoices. Preserves a supplied invoice number when it is unused and rejects existing invoice numbers rather than mutating issued documents. Optionally saves the column mapping for reuse. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"sourceColumn":{"type":"string","minLength":1},"targetField":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":100}},"required":["sourceColumn","targetField"],"additionalProperties":false}},"rows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}},"maxItems":1000},"skipErrors":{"type":"boolean","default":false},"saveMappingName":{"type":"string"}},"required":["mappings","rows"]}}}},"responses":{"200":{"description":"Import results with success/error counts and created/updated IDs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"totalRows":{"type":"number"},"successCount":{"type":"number"},"errorCount":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"row":{"type":"number"},"message":{"type":"string"}},"required":["row","message"]}},"createdIds":{"type":"array","items":{"type":"string"}},"updatedIds":{"type":"array","items":{"type":"string"}}},"required":["success","totalRows","successCount","errorCount","errors","createdIds","updatedIds"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/import/clients":{"post":{"tags":["Import"],"operationId":"importClients","summary":"Import clients from CSV data","description":"Imports clinic/client data from mapped CSV rows. Creates new clinic organizations and partnerships, or identifies existing ones. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"sourceColumn":{"type":"string","minLength":1},"targetField":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":100}},"required":["sourceColumn","targetField"],"additionalProperties":false}},"rows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}},"maxItems":1000},"skipErrors":{"type":"boolean","default":false},"saveMappingName":{"type":"string"}},"required":["mappings","rows"]}}}},"responses":{"200":{"description":"Import results with success/error counts and created/updated IDs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"totalRows":{"type":"number"},"successCount":{"type":"number"},"errorCount":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"row":{"type":"number"},"message":{"type":"string"}},"required":["row","message"]}},"createdIds":{"type":"array","items":{"type":"string"}},"updatedIds":{"type":"array","items":{"type":"string"}}},"required":["success","totalRows","successCount","errorCount","errors","createdIds","updatedIds"]}}}},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Client relationships changed during the import"}}}},"/import/products":{"post":{"tags":["Import"],"operationId":"importProducts","summary":"Import products from CSV data","description":"Imports product data from mapped CSV rows. Creates new products or updates existing ones matched by SKU. Auto-creates categories and subcategories as needed. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"sourceColumn":{"type":"string","minLength":1},"targetField":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":100}},"required":["sourceColumn","targetField"],"additionalProperties":false}},"rows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}},"maxItems":1000},"skipErrors":{"type":"boolean","default":false},"saveMappingName":{"type":"string"}},"required":["mappings","rows"]}}}},"responses":{"200":{"description":"Import results with success/error counts and created/updated IDs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"totalRows":{"type":"number"},"successCount":{"type":"number"},"errorCount":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"row":{"type":"number"},"message":{"type":"string"}},"required":["row","message"]}},"createdIds":{"type":"array","items":{"type":"string"}},"updatedIds":{"type":"array","items":{"type":"string"}}},"required":["success","totalRows","successCount","errorCount","errors","createdIds","updatedIds"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/import/price-lists":{"post":{"tags":["Import"],"operationId":"importPriceLists","summary":"Import price list items from CSV data","description":"Imports price list items from mapped CSV rows. Supports single or multiple price list mode. Matches products by SKU or name. Auto-creates price lists in multiple mode. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"sourceColumn":{"type":"string","minLength":1},"targetField":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":100}},"required":["sourceColumn","targetField"],"additionalProperties":false}},"rows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}},"maxItems":1000},"priceListId":{"type":"string"},"mode":{"type":"string","enum":["single","multiple"],"default":"single"},"skipErrors":{"type":"boolean","default":false},"saveMappingName":{"type":"string"}},"required":["mappings","rows"]}}}},"responses":{"200":{"description":"Import results with success/error counts and created/updated IDs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"totalRows":{"type":"number"},"successCount":{"type":"number"},"errorCount":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"row":{"type":"number"},"message":{"type":"string"}},"required":["row","message"]}},"createdIds":{"type":"array","items":{"type":"string"}},"updatedIds":{"type":"array","items":{"type":"string"}}},"required":["success","totalRows","successCount","errorCount","errors","createdIds","updatedIds"]}}}},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Price-list draft changed during import"}}}},"/import/mappings":{"get":{"tags":["Import"],"operationId":"listImportMappings","summary":"List saved import mappings","description":"Returns saved column mapping templates for the lab. Optionally filter by import type (products, clients, price_lists, balance_list). Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of saved import mappings","content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{}}},"required":["mappings"]}}}},"401":{"description":"Unauthorized — valid session required"}}},"post":{"tags":["Import"],"operationId":"createImportMapping","summary":"Save an import mapping template","description":"Saves a reusable column mapping template for future imports. Can be set as the default mapping for its import type. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"importType":{"type":"string","enum":["products","clients","price_lists","balance_list"]},"name":{"type":"string","minLength":1,"maxLength":100},"mappings":{"type":"array","items":{"type":"object","properties":{"sourceColumn":{"type":"string","minLength":1},"targetField":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":100}},"required":["sourceColumn","targetField"],"additionalProperties":false}},"isDefault":{"type":"boolean","default":false}},"required":["importType","name","mappings"]}}}},"responses":{"201":{"description":"Import mapping created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"mapping":{}}}}}},"401":{"description":"Unauthorized — valid session required"}}},"delete":{"tags":["Import"],"operationId":"deleteImportMapping","summary":"Delete an import mapping template","description":"Permanently deletes a saved import mapping template by ID (passed as query parameter). Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Import mapping deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Mapping not found"}}}},"/import/validate":{"post":{"tags":["Import"],"operationId":"validateImportData","summary":"Validate import data before importing","description":"Dry-run validation of mapped CSV rows for a given import type. Returns per-row validation results and a summary of valid/invalid counts without persisting anything. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"importType":{"type":"string","enum":["products","clients","price_lists","balance_list"]},"mappings":{"type":"array","items":{"type":"object","properties":{"sourceColumn":{"type":"string","minLength":1},"targetField":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":100}},"required":["sourceColumn","targetField"],"additionalProperties":false}},"rows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}},"maxItems":1000},"mode":{"type":"string","enum":["single","multiple"]},"priceListId":{"type":"string"}},"required":["importType","mappings","rows"]}}}},"responses":{"200":{"description":"Validation results with per-row errors and summary","content":{"application/json":{"schema":{"type":"object","properties":{"rowResults":{"type":"array","items":{"type":"object","properties":{"rowIndex":{"type":"number"},"isValid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}},"required":["field","message"]}},"data":{}},"required":["rowIndex","isValid","errors"]}},"summary":{"type":"object","properties":{"total":{"type":"number"},"valid":{"type":"number"},"invalid":{"type":"number"}},"required":["total","valid","invalid"]}},"required":["rowResults","summary"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/data-bundle/export":{"post":{"tags":["Data Bundle"],"operationId":"exportDataBundle","summary":"Export selected domains as a re-importable zip bundle","description":"Streams a versioned zip containing the selected configuration domains (data files + binary assets + manifest). Lab owners only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"domains":{"type":"array","items":{"type":"string","enum":["products","production","templates","lab_settings","clients","finance","users"]},"minItems":1},"includeFinanceTransactions":{"type":"boolean","default":false}},"required":["domains"]}}}},"responses":{"200":{"description":"Zip bundle download","content":{"application/zip":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Lab owners only"},"429":{"description":"Export budget exceeded"}}}},"/data-bundle/import/presign":{"post":{"tags":["Data Bundle"],"operationId":"presignDataBundleUpload","summary":"Get a presigned PUT URL for uploading a bundle zip","description":"Returns a presigned R2 upload URL for a bundle zip (max 250MB). The returned durable session ID is passed to analyze/execute; the storage key remains server-controlled. Lab owners only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fileName":{"type":"string","minLength":1,"maxLength":255},"size":{"type":"integer","exclusiveMinimum":0,"maximum":262144000}},"required":["fileName","size"]}}}},"responses":{"200":{"description":"Presigned upload URL","content":{"application/json":{"schema":{"type":"object","properties":{"uploadUrl":{"type":"string"},"sessionId":{"type":"string"},"uploadContentType":{"type":"string"}},"required":["uploadUrl","sessionId","uploadContentType"]}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Lab owners only"},"429":{"description":"Upload issuance budget exceeded"}}}},"/data-bundle/import/analyze":{"post":{"tags":["Data Bundle"],"operationId":"analyzeDataBundle","summary":"Dry-run an uploaded bundle against the current organization","description":"Parses the uploaded bundle and returns per-table created/updated/skipped counts plus warnings, without writing anything. Lab owners only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":"string","minLength":1}},"required":["sessionId"]}}}},"responses":{"200":{"description":"Dry-run result","content":{"application/json":{"schema":{"type":"object","properties":{"manifest":{"type":"object","properties":{"formatVersion":{"type":"integer"},"bundleId":{"type":"string"},"exportedAt":{"type":"string"},"source":{"type":"object","properties":{"orgId":{"type":"string"},"orgName":{"type":"string"},"orgType":{"type":"string"},"orgSlug":{"type":["string","null"],"minLength":1,"maxLength":50,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,48}[a-z0-9])?$"},"host":{"type":"string"}},"required":["orgId","orgName","orgType"]},"domains":{"type":"array","items":{"type":"string","enum":["products","production","templates","lab_settings","clients","finance","users"]}}},"required":["formatVersion","bundleId","exportedAt","source","domains"]},"sameOrg":{"type":"boolean"},"crossEnv":{"type":"boolean"},"tables":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"domain":{"type":"string","enum":["products","production","templates","lab_settings","clients","finance","users"]},"importable":{"type":"boolean"},"rowsInBundle":{"type":"integer","minimum":0},"toCreate":{"type":"integer","minimum":0},"toUpdate":{"type":"integer","minimum":0},"toSkip":{"type":"integer","minimum":0},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["member_not_found","fk_missing","env_specific_stripped","columns_drift","export_only_table","json_id_dropped","ambiguous_match","tray_code_missing","row_invalid","asset_missing"]},"message":{"type":"string"},"table":{"type":"string"},"count":{"type":"integer"},"sample":{"type":"array","items":{"type":"string"}}},"required":["code","message"]}}},"required":["name","domain","importable","rowsInBundle","toCreate","toUpdate","toSkip","warnings"]}},"assets":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"totalBytes":{"type":"integer","minimum":0}},"required":["count","totalBytes"]},"blockers":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["format_version_unsupported","wrong_org_type","manifest_invalid","bundle_too_large","target_table_too_large","import_in_progress","category_cycle","target_organization_missing","catalog_money_invalid"]},"message":{"type":"string"}},"required":["code","message"]}}},"required":["manifest","sameOrg","crossEnv","tables","assets","blockers"]}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Lab owners only"},"404":{"description":"Uploaded bundle not found"},"409":{"description":"Import session is terminal or already in use"}}}},"/data-bundle/import/execute":{"post":{"tags":["Data Bundle"],"operationId":"executeDataBundleImport","summary":"Import an uploaded bundle into the current organization","description":"Re-plans the uploaded bundle and applies it: all ids are regenerated, references remapped, assets copied. One import at a time per organization. Lab owners only.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":"string","minLength":1},"domains":{"type":"array","items":{"type":"string","enum":["products","production","templates","lab_settings","clients","finance","users"]},"minItems":1},"conflictStrategy":{"type":"string","enum":["update","skip"],"default":"update"}},"required":["sessionId"]}}}},"responses":{"200":{"description":"Import result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"importLogId":{"type":"string"},"tables":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"domain":{"type":"string","enum":["products","production","templates","lab_settings","clients","finance","users"]},"created":{"type":"integer","minimum":0},"updated":{"type":"integer","minimum":0},"skipped":{"type":"integer","minimum":0},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["member_not_found","fk_missing","env_specific_stripped","columns_drift","export_only_table","json_id_dropped","ambiguous_match","tray_code_missing","row_invalid","asset_missing"]},"message":{"type":"string"},"table":{"type":"string"},"count":{"type":"integer"},"sample":{"type":"array","items":{"type":"string"}}},"required":["code","message"]}}},"required":["name","domain","created","updated","skipped","warnings"]}},"totals":{"type":"object","properties":{"created":{"type":"integer","minimum":0},"updated":{"type":"integer","minimum":0},"skipped":{"type":"integer","minimum":0}},"required":["created","updated","skipped"]},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["member_not_found","fk_missing","env_specific_stripped","columns_drift","export_only_table","json_id_dropped","ambiguous_match","tray_code_missing","row_invalid","asset_missing"]},"message":{"type":"string"},"table":{"type":"string"},"count":{"type":"integer"},"sample":{"type":"array","items":{"type":"string"}}},"required":["code","message"]}},"assetsCopied":{"type":"integer","minimum":0}},"required":["success","tables","totals","warnings","assetsCopied"]}}}},"400":{"description":"Invalid request or bundle has blockers"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Lab owners only"},"404":{"description":"Uploaded bundle not found"},"409":{"description":"Another import is already running"}}}},"/ai/generate":{"post":{"tags":["AI"],"operationId":"generateAiMessage","summary":"Generate an AI message for a conversation","description":"Uses GPT-4o-mini to generate a contextually appropriate message for a conversation after explicit data-processing consent. Returns a Server-Sent Events stream. Rate limited to 10 requests per minute per user.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1},"dataProcessingConsent":{"type":"boolean","enum":[true]},"instruction":{"type":"string","minLength":1,"maxLength":1000}},"required":["conversationId","dataProcessingConsent"],"additionalProperties":false}}}},"responses":{"200":{"description":"SSE stream of generated message tokens","content":{"text/event-stream":{"schema":{}}}},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Conversation deletion has been authorized"},"413":{"description":"AI prompt input limit exceeded"},"429":{"description":"Global, organization, or user AI usage limit exceeded"}}}},"/ai/autocomplete":{"post":{"tags":["AI"],"operationId":"autocompleteAiMessage","summary":"Autocomplete a partially typed message","description":"Uses GPT-4o-mini to suggest a completion for a partially typed conversation message after explicit data-processing consent. Requires at least 10 characters of input. Rate limited to 30 requests per minute per user.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1},"currentText":{"type":"string","minLength":10,"maxLength":2000},"dataProcessingConsent":{"type":"boolean","enum":[true]}},"required":["conversationId","currentText","dataProcessingConsent"],"additionalProperties":false}}}},"responses":{"200":{"description":"Suggested message completion text","content":{"application/json":{"schema":{"type":"object","properties":{"completion":{"type":"string"}},"required":["completion"]}}}},"401":{"description":"Unauthorized — valid session required"},"409":{"description":"Conversation deletion has been authorized"},"413":{"description":"AI prompt input limit exceeded"},"429":{"description":"Global, organization, or user AI usage limit exceeded"}}}},"/search":{"get":{"tags":["Search"],"operationId":"searchGlobal","summary":"Global search across all entities","description":"Performs a fuzzy search across orders, patients, organizations, products (lab only), invoices, and team members within the authenticated organization's scope. Returns ranked results grouped by entity type.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":2,"maxLength":100,"example":"search term"},"required":true,"name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":10,"default":5,"example":5},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Search results grouped by entity type with relevance scores","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"object","properties":{"orders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"subtitle":{"type":["string","null"]},"url":{"type":"string"},"relevance":{"type":"number"}},"required":["id","label","subtitle","url","relevance"]}},"patients":{"type":"array","items":{}},"organizations":{"type":"array","items":{}},"products":{"type":"array","items":{}},"invoices":{"type":"array","items":{}},"members":{"type":"array","items":{}}},"required":["orders","patients","organizations","products","invoices","members"]},"query":{"type":"string"},"degraded":{"type":"array","items":{"type":"string"},"description":"Categories whose query failed and degraded to an empty list. Empty when every category ran successfully.","example":[]}},"required":["results","query","degraded"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/download/{software}/{platform}":{"get":{"tags":["Download"],"operationId":"downloadSoftwareInstaller","summary":"Download software installer for a platform","description":"Fetches the newest matching software release asset from the private release repository and streams it to an authenticated user.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["uploader"],"example":"uploader"},"required":true,"name":"software","in":"path"},{"schema":{"type":"string","enum":["mac-arm","mac-intel","windows"],"example":"mac-arm"},"required":true,"name":"platform","in":"path"}],"responses":{"200":{"description":"Binary file download stream","content":{"application/octet-stream":{"schema":{}}}},"302":{"description":"Redirect to a short-lived signed release asset URL"},"400":{"description":"Unknown software or invalid platform"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"No matching release asset found"},"413":{"description":"Release asset exceeds the download ceiling"},"429":{"description":"Release lookup budget exhausted"},"502":{"description":"Release service unavailable"}}}},"/download/uploader/update/{target}/{arch}/{currentVersion}":{"get":{"tags":["Download"],"operationId":"checkUploaderUpdate","summary":"Check for a signed uploader update","description":"Returns signed Tauri updater metadata for the authenticated device, or no content when its installed version is current.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["darwin","windows"]},"required":true,"name":"target","in":"path"},{"schema":{"type":"string","enum":["aarch64","x86_64"]},"required":true,"name":"arch","in":"path"},{"schema":{"type":"string","pattern":"^(?:v)?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)$"},"required":true,"name":"currentVersion","in":"path"}],"responses":{"200":{"description":"Signed updater metadata","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string"},"pub_date":{"type":"string"},"url":{"type":"string","format":"uri"},"signature":{"type":"string"},"notes":{"type":"string"}},"required":["version","url","signature"]}}}},"204":{"description":"The installed version is current"},"400":{"description":"Unsupported target or invalid version"},"401":{"description":"Unauthorized — native device credential required"},"404":{"description":"No signed update found"},"413":{"description":"Release asset exceeds the download ceiling"},"429":{"description":"Release lookup budget exhausted"},"502":{"description":"Release service unavailable"}}}},"/download/uploader/update-asset/{version}/{assetName}":{"get":{"tags":["Download"],"operationId":"downloadUploaderUpdateAsset","summary":"Download a signed uploader update bundle","description":"Streams an allowlisted updater bundle from an exact private release version to an authenticated device.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)$"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","enum":["GuideLab-Uploader_aarch64.app.tar.gz","GuideLab-Uploader_x64.app.tar.gz","GuideLab-Uploader_x64.exe"]},"required":true,"name":"assetName","in":"path"}],"responses":{"200":{"description":"Signed updater bundle","content":{"application/octet-stream":{"schema":{}}}},"302":{"description":"Redirect to a short-lived signed release asset URL"},"400":{"description":"Unsupported updater asset"},"401":{"description":"Unauthorized — native device credential required"},"404":{"description":"Update asset not found"},"413":{"description":"Release asset exceeds the download ceiling"},"429":{"description":"Release lookup budget exhausted"},"502":{"description":"Release service unavailable"}}}},"/clinic/labs":{"get":{"tags":["Clinic"],"operationId":"getClinicPartnerLabs","summary":"List partner labs for the clinic","description":"Returns all labs that have a partnership with the authenticated clinic, including lab contact details and partnership status. Clinic-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of partner labs with contact info and partnership status","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"labName":{"type":"string"},"labLogo":{"type":["string","null"]},"labCity":{"type":["string","null"]},"labPhone":{"type":["string","null"]},"labEmail":{"type":["string","null"]},"status":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","labId","labName","labLogo","labCity","labPhone","labEmail","status","createdAt"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized — valid session required"}}}},"/clients":{"get":{"tags":["Clients"],"operationId":"listClients","summary":"List clinic clients (doctors) for a lab","description":"Returns paginated list of clinic owner members (doctors) from partner clinics. Supports search, filtering by clinic ID, and sorting by name, email, practice, or creation date. Includes per-doctor order counts. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Paginated list of client members with order counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"userName":{"type":"string"},"userEmail":{"type":"string"},"userPhone":{"type":["string","null"]},"role":{"type":"string"},"clinicId":{"type":"string"},"clinicName":{"type":"string"},"orderCount":{"type":"number"},"createdAt":{"type":"string"}},"required":["id","userId","userName","userEmail","userPhone","role","clinicId","clinicName","orderCount","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can access this endpoint"}}}},"/automations":{"get":{"operationId":"listAutomations","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false},"required":false,"name":"includeInactive","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["order_created","order_status_changed","product_added_to_order","manual","qc_completed","scheduled_invoice_generation"]},"required":false,"name":"triggerType","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated automations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query"},"403":{"description":"Lab only"}}},"post":{"operationId":"createAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"triggerType":{"type":"string","enum":["order_created","order_status_changed","product_added_to_order","manual","qc_completed","scheduled_invoice_generation"]},"triggerConfig":{"type":"object","additionalProperties":{}},"flowDefinition":{"type":"object","properties":{"nodes":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"additionalProperties":false},"type":{"type":"string","enum":["trigger"]},"data":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200},"triggerType":{"type":"string","enum":["order_created","order_status_changed","product_added_to_order","manual","qc_completed","scheduled_invoice_generation"]},"config":{"type":"object","additionalProperties":{},"default":{}}},"required":["label","triggerType"],"additionalProperties":false}},"required":["id","position","type","data"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"additionalProperties":false},"type":{"type":"string","enum":["condition"]},"data":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200},"field":{"type":"string","minLength":1,"maxLength":200},"operator":{"type":"string","enum":["equals","not_equals","contains","not_contains","greater_than","less_than","is_empty","is_not_empty"]},"value":{"type":"string","maxLength":2000}},"required":["label","field","operator","value"],"additionalProperties":false}},"required":["id","position","type","data"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"additionalProperties":false},"type":{"type":"string","enum":["action"]},"data":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200},"actionType":{"type":"string","enum":["log_message","update_order_field","send_notification","send_email","webhook","generate_draft_invoices"]},"config":{"type":"object","additionalProperties":{},"default":{}}},"required":["label","actionType"],"additionalProperties":false}},"required":["id","position","type","data"],"additionalProperties":false}]},"maxItems":50},"edges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"source":{"type":"string","minLength":1,"maxLength":100},"target":{"type":"string","minLength":1,"maxLength":100},"sourceHandle":{"type":["string","null"],"maxLength":100},"targetHandle":{"type":["string","null"],"maxLength":100},"label":{"type":"string","maxLength":200}},"required":["id","source","target"],"additionalProperties":false},"maxItems":100}},"required":["nodes","edges"],"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0,"default":0}},"required":["name","triggerType"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created automation draft","content":{"application/json":{"schema":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]}},"required":["automation"]}}}},"400":{"description":"Invalid body"},"403":{"description":"Lab only"}}}},"/automations/{id}":{"get":{"operationId":"getAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Automation","content":{"application/json":{"schema":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]}},"required":["automation"]}}}},"403":{"description":"Lab only"},"404":{"description":"Not found"}}},"put":{"operationId":"updateAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500},"triggerType":{"type":"string","enum":["order_created","order_status_changed","product_added_to_order","manual","qc_completed","scheduled_invoice_generation"]},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","properties":{"nodes":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"additionalProperties":false},"type":{"type":"string","enum":["trigger"]},"data":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200},"triggerType":{"type":"string","enum":["order_created","order_status_changed","product_added_to_order","manual","qc_completed","scheduled_invoice_generation"]},"config":{"type":"object","additionalProperties":{},"default":{}}},"required":["label","triggerType"],"additionalProperties":false}},"required":["id","position","type","data"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"additionalProperties":false},"type":{"type":"string","enum":["condition"]},"data":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200},"field":{"type":"string","minLength":1,"maxLength":200},"operator":{"type":"string","enum":["equals","not_equals","contains","not_contains","greater_than","less_than","is_empty","is_not_empty"]},"value":{"type":"string","maxLength":2000}},"required":["label","field","operator","value"],"additionalProperties":false}},"required":["id","position","type","data"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"required":["x","y"],"additionalProperties":false},"type":{"type":"string","enum":["action"]},"data":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200},"actionType":{"type":"string","enum":["log_message","update_order_field","send_notification","send_email","webhook","generate_draft_invoices"]},"config":{"type":"object","additionalProperties":{},"default":{}}},"required":["label","actionType"],"additionalProperties":false}},"required":["id","position","type","data"],"additionalProperties":false}]},"maxItems":50},"edges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":100},"source":{"type":"string","minLength":1,"maxLength":100},"target":{"type":"string","minLength":1,"maxLength":100},"sourceHandle":{"type":["string","null"],"maxLength":100},"targetHandle":{"type":["string","null"],"maxLength":100},"label":{"type":"string","maxLength":200}},"required":["id","source","target"],"additionalProperties":false},"maxItems":100}},"required":["nodes","edges"],"additionalProperties":false},"sortOrder":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated draft","content":{"application/json":{"schema":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]}},"required":["automation"]}}}},"400":{"description":"Invalid body"},"403":{"description":"Lab only"},"404":{"description":"Not found"},"409":{"description":"Invalid or archived draft"}}},"delete":{"operationId":"deleteAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Archived automation","content":{"application/json":{"schema":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["automation","message"]}}}},"403":{"description":"Lab only"},"404":{"description":"Not found"}}},"patch":{"operationId":"toggleAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isActive":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Changed activation","content":{"application/json":{"schema":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]}},"required":["automation"]}}}},"400":{"description":"Draft is not executable"},"403":{"description":"Lab only"},"404":{"description":"Not found"},"409":{"description":"Archived"}}}},"/automations/{id}/publish":{"post":{"operationId":"publishAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"revisionKind":{"type":"string","enum":["published","test"],"default":"published"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Published immutable revision","content":{"application/json":{"schema":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"triggerType":{"type":"string"},"triggerConfig":{"type":["object","null"],"additionalProperties":{}},"flowDefinition":{"type":"object","additionalProperties":{}},"activeRevisionId":{"type":["string","null"]},"archivedAt":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","name","description","triggerType","triggerConfig","flowDefinition","activeRevisionId","archivedAt","isActive","sortOrder","createdAt","updatedAt"]}},"required":["automation"]}}}},"400":{"description":"Draft is not executable"},"403":{"description":"Lab only"},"404":{"description":"Not found"},"409":{"description":"Archived"}}}},"/automations/{id}/executions":{"get":{"operationId":"listAutomationExecutions","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["queued","running","succeeded","failed","skipped","timed_out","cancelled"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Immutable execution history","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"automationId":{"type":"string"},"revisionId":{"type":"string"},"labId":{"type":"string"},"triggerKey":{"type":"string"},"triggerPayload":{"type":["object","null"],"additionalProperties":{}},"acceptedByUserId":{"type":["string","null"]},"status":{"type":"string"},"attemptCount":{"type":"number"},"queuedAt":{"type":"string"},"startedAt":{"type":["string","null"]},"heartbeatAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["id","automationId","revisionId","labId","triggerKey","triggerPayload","acceptedByUserId","status","attemptCount","queuedAt","startedAt","heartbeatAt","completedAt","error"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"403":{"description":"Lab only"}}}},"/automations/{id}/run":{"post":{"operationId":"runAutomation","tags":["Automations"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"triggerKey":{"type":"string","minLength":1,"maxLength":200},"triggerPayload":{"type":"object","additionalProperties":{}}},"required":["triggerKey"],"additionalProperties":false}}}},"responses":{"202":{"description":"Execution durably accepted","content":{"application/json":{"schema":{"type":"object","properties":{"execution":{"type":"object","properties":{"id":{"type":"string"},"automationId":{"type":"string"},"revisionId":{"type":"string"},"labId":{"type":"string"},"triggerKey":{"type":"string"},"triggerPayload":{"type":["object","null"],"additionalProperties":{}},"acceptedByUserId":{"type":["string","null"]},"status":{"type":"string"},"attemptCount":{"type":"number"},"queuedAt":{"type":"string"},"startedAt":{"type":["string","null"]},"heartbeatAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["id","automationId","revisionId","labId","triggerKey","triggerPayload","acceptedByUserId","status","attemptCount","queuedAt","startedAt","heartbeatAt","completedAt","error"]},"duplicate":{"type":"boolean"}},"required":["execution","duplicate"]}}}},"400":{"description":"Invalid body or trigger"},"403":{"description":"Lab only"},"404":{"description":"Not found"},"409":{"description":"Inactive or conflicting trigger key"},"429":{"description":"Manual automation acceptance budget exhausted"}}}},"/archive/orders":{"get":{"tags":["Archive"],"operationId":"listArchivedOrders","summary":"List archived orders","description":"Returns paginated archived orders for active clinic partnerships. Hidden patient names are masked and excluded from search. Supports filtering by clinic ID. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Paginated list of archived orders with patient and clinic info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"},"archivedAt":{"type":"string"},"archivedByUser":{},"archivedByUserNameMissing":{"type":"boolean"},"patient":{},"patientFirstNameMissing":{"type":"boolean"},"clinic":{}},"required":["id","orderNumber","status","createdAt","archivedAt","archivedByUserNameMissing","patientFirstNameMissing"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can access the archive"}}},"post":{"tags":["Archive"],"operationId":"archiveOrder","summary":"Archive an order","description":"Archives a completed or cancelled order by setting its archivedAt timestamp. Shipment status is independent. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Order archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"order":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"archivedAt":{"type":"string"}},"required":["id","orderNumber"]}},"required":["success","order"]}}}},"400":{"description":"Order is already archived or has invalid status"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Order not found"},"409":{"description":"Order changed before it could be archived"}}}},"/archive/orders/restore":{"post":{"tags":["Archive"],"operationId":"restoreArchivedOrder","summary":"Restore an archived order","description":"Removes the archived status from an order, making it active again. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Order restored successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"order":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string"}},"required":["id","orderNumber","status"]}},"required":["success","order"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Archived order not found"},"409":{"description":"Order changed before it could be restored"}}}},"/archive/patients":{"get":{"tags":["Archive"],"operationId":"listArchivedPatients","summary":"List archived patients","description":"Returns paginated archived patients whose PII is shared through an active partnership or an explicit patient grant. Inaccessible patients are omitted. Supports search and clinic filtering. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Paginated list of archived patients with clinic and order info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clinicId":{"type":["string","null"]},"firstName":{"type":"string"},"lastName":{"type":"string"},"fullName":{"type":"string"},"externalId":{"type":["string","null"]},"clinic":{},"orderCount":{"type":"number"},"createdAt":{"type":"string"},"archivedAt":{"type":"string"},"archivedByUser":{},"archivedByUserNameMissing":{"type":"boolean"}},"required":["id","clinicId","firstName","lastName","fullName","externalId","orderCount","createdAt","archivedAt","archivedByUserNameMissing"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can access the archive"}}},"post":{"tags":["Archive"],"operationId":"archivePatient","summary":"Archive a patient","description":"Archives a patient by setting its archivedAt timestamp. Fails if the patient has active (non-archived) orders. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Patient archived successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"patient":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"archivedAt":{"type":"string"}},"required":["id","firstName","lastName"]},"warning":{"type":["string","null"]}},"required":["success","patient","warning"]}}}},"400":{"description":"Patient already archived or has active orders"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Patient not found"},"409":{"description":"Patient changed before it could be archived"}}}},"/archive/patients/restore":{"post":{"tags":["Archive"],"operationId":"restoreArchivedPatient","summary":"Restore an archived patient","description":"Removes the archived status from a patient, making them active again. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Patient restored successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"patient":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"required":["id","firstName","lastName"]}},"required":["success","patient"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Archived patient not found"},"409":{"description":"Patient changed before it could be restored"}}}},"/forms":{"get":{"tags":["Forms"],"operationId":"listForms","summary":"List forms with questions","description":"Returns forms for the authenticated lab with their associated questions. Supports filtering by scope, search, and active status. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of forms with embedded questions","content":{"application/json":{"schema":{"type":"object","properties":{"forms":{"type":"array","items":{}}},"required":["forms"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage forms"}}},"post":{"tags":["Forms"],"operationId":"createForm","summary":"Create a new form","description":"Creates a new form with optional initial question assignments. Auto-assigns sort order if not specified. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"201":{"description":"Form created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"form":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage forms"}}}},"/forms/settings":{"get":{"tags":["Forms"],"operationId":"getFormSettings","summary":"Get form feature settings","description":"Returns the lab's form settings including whether order flow forms and registration forms are enabled. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Form settings object","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage form settings"}}},"put":{"tags":["Forms"],"operationId":"updateFormSettings","summary":"Update form feature settings","description":"Updates the lab's form settings. Creates the settings record if it does not exist. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Form settings updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage form settings"}}}},"/forms/questions":{"get":{"tags":["Forms"],"operationId":"listFormQuestions","summary":"List all form questions","description":"Returns paginated form questions for the authenticated lab. Supports filtering by scope, search, and active status. These are global questions that can be assigned to multiple forms. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"List of form questions","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{}}},"required":["questions"]}}}},"400":{"description":"Invalid query parameters"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage form questions"}}},"post":{"tags":["Forms"],"operationId":"createFormQuestion","summary":"Create a form question","description":"Creates a new reusable form question. Auto-assigns sort order if not specified. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","enum":["order_flow","registration"]},"label":{"type":"string","minLength":1,"maxLength":200},"fieldType":{"type":"string","enum":["text","textarea","number","date","select","multiselect","checkbox","radio"]},"placeholder":{"type":"string","maxLength":200},"helpText":{"type":"string","maxLength":500},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":200},"label":{"type":"string","minLength":1,"maxLength":200}},"required":["value","label"],"additionalProperties":false},"maxItems":100},"isRequired":{"type":"boolean","default":false},"sortOrder":{"type":"integer","minimum":0,"default":0},"isActive":{"type":"boolean","default":true}},"required":["scope","label","fieldType"],"additionalProperties":false}}}},"responses":{"201":{"description":"Form question created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"question":{}}}}}},"400":{"description":"Invalid form question definition"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage form questions"},"413":{"description":"Question definition exceeds the raw-body ceiling"}}}},"/forms/questions/reorder":{"put":{"tags":["Forms"],"operationId":"reorderFormQuestions","summary":"Reorder form questions globally","description":"Updates the sort order of multiple form questions at once. All specified questions must belong to the authenticated lab. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Questions reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{}}},"required":["questions"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can manage form questions"}}}},"/forms/questions/{id}":{"get":{"tags":["Forms"],"operationId":"getFormQuestion","summary":"Get a form question by ID","description":"Returns a single form question by ID, scoped to the authenticated lab. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form question ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form question ID","name":"id","in":"path"}],"responses":{"200":{"description":"Form question details","content":{"application/json":{"schema":{"type":"object","properties":{"question":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Question not found"}}},"put":{"tags":["Forms"],"operationId":"updateFormQuestion","summary":"Update a form question","description":"Updates a form question's label, field type, options, or other properties. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form question ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form question ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","enum":["order_flow","registration"]},"label":{"type":"string","minLength":1,"maxLength":200},"fieldType":{"type":"string","enum":["text","textarea","number","date","select","multiselect","checkbox","radio"]},"placeholder":{"type":"string","maxLength":200},"helpText":{"type":"string","maxLength":500},"options":{"type":["array","null"],"items":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":200},"label":{"type":"string","minLength":1,"maxLength":200}},"required":["value","label"],"additionalProperties":false},"maxItems":100},"isRequired":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":0},"isActive":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Form question updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"question":{}}}}}},"400":{"description":"Invalid form question definition"},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Question not found"},"409":{"description":"Question scope cannot change while it is assigned to a form"},"413":{"description":"Question definition exceeds the raw-body ceiling"}}},"delete":{"tags":["Forms"],"operationId":"deleteFormQuestion","summary":"Deactivate a form question","description":"Soft-deletes a form question by setting it to inactive. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form question ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form question ID","name":"id","in":"path"}],"responses":{"200":{"description":"Form question deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"question":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Question not found"}}}},"/forms/{id}/publish":{"post":{"tags":["Forms"],"operationId":"publishFormRevision","summary":"Publish an immutable form revision","description":"Resolves the current element assignments into a self-contained immutable revision and activates it atomically.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Published revision","content":{"application/json":{"schema":{"type":"object","properties":{"revision":{}}}}}},"400":{"description":"The draft cannot be published"},"403":{"description":"Only lab managers can publish forms"},"404":{"description":"Form not found"}}}},"/forms/{id}/orders/{orderId}/submission":{"post":{"tags":["Forms"],"operationId":"submitOrderForm","summary":"Submit answers for an order form","description":"Validates one immutable JSON answer document against the active pinned order-flow revision.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"orderId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"answers":{"type":"object","additionalProperties":{}}},"required":["answers"],"additionalProperties":false}}}},"responses":{"201":{"description":"Submission persisted"},"400":{"description":"Answers do not satisfy the pinned revision"},"403":{"description":"Organization is not an order participant"},"404":{"description":"Order or form not found"},"409":{"description":"Form is unpublished or already submitted"},"413":{"description":"Answer document exceeds the raw-body ceiling"}}}},"/forms/{id}/partnerships/{partnershipId}/registration-submission":{"post":{"tags":["Forms"],"operationId":"submitPartnershipRegistrationForm","summary":"Submit answers for a partnership registration form","description":"Validates one immutable JSON answer document against the active pinned registration revision.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"partnershipId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"answers":{"type":"object","additionalProperties":{}}},"required":["answers"],"additionalProperties":false}}}},"responses":{"201":{"description":"Submission persisted"},"400":{"description":"Answers do not satisfy the pinned revision"},"403":{"description":"Organization is not a partnership participant"},"404":{"description":"Partnership or form not found"},"409":{"description":"Form is unpublished or already submitted"},"413":{"description":"Answer document exceeds the raw-body ceiling"}}}},"/forms/{id}":{"get":{"tags":["Forms"],"operationId":"getForm","summary":"Get a form by ID","description":"Returns a single form with all its associated questions, scoped to the authenticated lab. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"Form details with associated questions","content":{"application/json":{"schema":{"type":"object","properties":{"form":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}},"put":{"tags":["Forms"],"operationId":"updateForm","summary":"Update a form","description":"Updates a form's properties and optionally replaces its question assignments. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"Form updated successfully with questions","content":{"application/json":{"schema":{"type":"object","properties":{"form":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}},"delete":{"tags":["Forms"],"operationId":"deleteForm","summary":"Deactivate a form","description":"Soft-deletes a form by setting it to inactive. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"Form deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"form":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}}},"/forms/{id}/questions":{"get":{"tags":["Forms"],"operationId":"listFormAssignedQuestions","summary":"List questions assigned to a form","description":"Returns all questions assigned to a specific form, ordered by sort order. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of assigned questions with sort order","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{}}},"required":["questions"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}},"post":{"tags":["Forms"],"operationId":"addQuestionsToForm","summary":"Add questions to a form","description":"Assigns existing questions to a form. Skips questions already assigned. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"Updated list of assigned questions","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{}}},"required":["questions"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}},"put":{"tags":["Forms"],"operationId":"reorderFormAssignedQuestions","summary":"Reorder questions within a form","description":"Updates the sort order of questions assigned to a specific form. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"Questions reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{}}},"required":["questions"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}},"delete":{"tags":["Forms"],"operationId":"removeQuestionFromForm","summary":"Remove a question from a form","description":"Removes the association between a question and a form. The question itself is not deleted. Expects elementId in the request body. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Form ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Form ID","name":"id","in":"path"}],"responses":{"200":{"description":"Question removed from form successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Form not found"}}}},"/location-routes":{"get":{"tags":["Location Routes"],"operationId":"listLocationRoutes","summary":"List location routes","description":"Returns paginated delivery location routes for a lab. Labs see their own routes; clinics must specify a labId query parameter and must be active partners of that lab.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Paginated list of location routes","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"}}},"post":{"tags":["Location Routes"],"operationId":"createLocationRoute","summary":"Create a location route","description":"Creates a new delivery location route. If set as default, the previous default is unset. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"201":{"description":"Location route created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"route":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can create location routes"}}}},"/location-routes/{id}":{"get":{"tags":["Location Routes"],"operationId":"getLocationRoute","summary":"Get a location route by ID","description":"Returns a single location route by ID. Labs see their own routes; clinics can only access routes from partner labs.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Location route ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Location route ID","name":"id","in":"path"}],"responses":{"200":{"description":"Location route details","content":{"application/json":{"schema":{"type":"object","properties":{"route":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Location route not found"}}},"put":{"tags":["Location Routes"],"operationId":"updateLocationRoute","summary":"Update a location route","description":"Updates a location route's properties. If set as default, the previous default is unset. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Location route ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Location route ID","name":"id","in":"path"}],"responses":{"200":{"description":"Location route updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"route":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can update location routes"},"404":{"description":"Location route not found"}}},"delete":{"tags":["Location Routes"],"operationId":"deleteLocationRoute","summary":"Deactivate a location route","description":"Soft-deletes a location route by setting it to inactive. Cannot delete the default route. Lab-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Location route ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Location route ID","name":"id","in":"path"}],"responses":{"200":{"description":"Location route deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"route":{},"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Cannot delete the default route"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only labs can delete location routes"},"404":{"description":"Location route not found"}}}},"/clinic-locations":{"get":{"tags":["Clinic Locations"],"operationId":"listClinicLocations","summary":"List clinic locations","description":"Returns paginated clinic locations. Clinics see their own locations; labs must specify a clinicId query parameter and must have an active partnership with that clinic.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Paginated list of clinic locations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Access denied to this clinic's locations"}}},"post":{"tags":["Clinic Locations"],"operationId":"createClinicLocation","summary":"Create a clinic location","description":"Creates a new physical location for the authenticated clinic. If set as default, the previous default is unset. Clinic-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"201":{"description":"Clinic location created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"location":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only clinics can create locations"}}}},"/clinic-locations/{id}":{"get":{"tags":["Clinic Locations"],"operationId":"getClinicLocation","summary":"Get a clinic location by ID","description":"Returns a single clinic location by ID.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Clinic location ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Clinic location ID","name":"id","in":"path"}],"responses":{"200":{"description":"Clinic location details","content":{"application/json":{"schema":{"type":"object","properties":{"location":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"404":{"description":"Location not found"}}},"put":{"tags":["Clinic Locations"],"operationId":"updateClinicLocation","summary":"Update a clinic location","description":"Updates a clinic location's properties. If set as default, the previous default is unset. Clinic-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Clinic location ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Clinic location ID","name":"id","in":"path"}],"responses":{"200":{"description":"Clinic location updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"location":{}}}}}},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only clinics can update locations"},"404":{"description":"Location not found"}}},"delete":{"tags":["Clinic Locations"],"operationId":"deleteClinicLocation","summary":"Deactivate a clinic location","description":"Soft-deletes a clinic location by setting it to inactive. Cannot delete the default location. Clinic-only endpoint.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Clinic location ID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Clinic location ID","name":"id","in":"path"}],"responses":{"200":{"description":"Location deactivated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"location":{},"message":{"type":"string"}},"required":["message"]}}}},"400":{"description":"Cannot delete the default location"},"401":{"description":"Unauthorized — valid session required"},"403":{"description":"Only clinics can delete locations"},"404":{"description":"Location not found"}}}},"/webhooks/stripe-connect":{"post":{"tags":["Webhooks"],"operationId":"handleStripeConnectWebhook","summary":"Handle Stripe Connect webhook events","description":"Receives verified Stripe Connect account, PaymentIntent, refund, and dispute events. Public endpoint; provider signature authentication is required outside explicit local test mode.","security":[],"responses":{"200":{"description":"Webhook event processed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}},"required":["received"]}}}},"400":{"description":"Invalid signature, payload, mode, or provenance"},"413":{"description":"Webhook body exceeds the allowed size"},"500":{"description":"Webhook processing failed"},"503":{"description":"Webhook authentication is not configured safely"}}}},"/webhooks/medit":{"post":{"tags":["Webhooks"],"operationId":"handleMeditWebhook","summary":"Handle Medit scanner webhook events","description":"Receives Medit Link webhook events and requires the connection-specific HMAC signature. Public endpoint; no session authentication.","security":[],"responses":{"200":{"description":"Webhook event acknowledged","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Invalid JSON payload"},"401":{"description":"Missing or invalid webhook signature"},"413":{"description":"Webhook body exceeds the allowed size"},"500":{"description":"Webhook evidence could not be stored"},"503":{"description":"Webhook signature is not configured"}}}},"/webhooks/ship-engine/{integrationId}/{token}":{"post":{"tags":["Webhooks"],"operationId":"handleShipEngineTrackingWebhook","summary":"Handle ShipEngine tracking webhook events","description":"Receives exact ShipEngine API_TRACK payloads authenticated by a per-integration URL token and ShipEngine RSA signature.","security":[],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"integrationId","in":"path"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$/u"},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Webhook evidence accepted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"duplicate":{"type":"boolean"}},"required":["ok","duplicate"]}}}},"400":{"description":"Invalid webhook payload"},"401":{"description":"Missing, invalid, or expired provider signature"},"404":{"description":"Webhook endpoint not found"},"413":{"description":"Webhook body exceeds the allowed size"},"429":{"description":"Webhook verification budget exhausted"},"500":{"description":"Webhook evidence could not be stored"}}}},"/work-trays":{"get":{"operationId":"listWorkTrays","description":"List work trays for the current lab with pagination, search, and optional order filtering. Includes the currently assigned order for each tray. Lab-only operation.","tags":["Work Trays"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":50,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"search term"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"orderId","in":"query"},{"schema":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"default":false,"example":"false"},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"Paginated list of work trays with current order assignments","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"code":{"type":"string"},"color":{"type":["string","null"]},"description":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"currentOrderId":{"type":["string","null"]},"currentOrderNumber":{"type":["string","null"]},"currentOrderPhaseId":{"type":["string","null"]},"currentPhaseName":{"type":["string","null"]}},"required":["id","labId","code","description","isActive","sortOrder","createdAt","updatedAt","currentOrderId","currentOrderNumber","currentOrderPhaseId","currentPhaseName"]}},"pagination":{"type":"object","properties":{"page":{"type":"number"},"limit":{"type":"number"},"total":{"type":"number"},"totalPages":{"type":"number"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"]}}}},"400":{"description":"Invalid query parameters"},"403":{"description":"Only labs can access work trays"},"500":{"description":"Internal server error"}}},"post":{"operationId":"createWorkTray","description":"Create a new work tray with a unique code. Lab-only operation. Returns 409 if a tray with the same code already exists.","tags":["Work Trays"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":50},"color":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean","default":true},"sortOrder":{"type":"integer","default":0}},"required":["code"]}}}},"responses":{"201":{"description":"Newly created work tray","content":{"application/json":{"schema":{"type":"object","properties":{"tray":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"code":{"type":"string"},"color":{"type":["string","null"]},"description":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","code","description","isActive","sortOrder","createdAt","updatedAt"]}},"required":["tray"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can create work trays"},"409":{"description":"A tray with this code already exists"},"500":{"description":"Internal server error"}}}},"/work-trays/batch":{"post":{"operationId":"batchCreateWorkTrays","description":"Bulk-create work trays from a pattern and quantity. Generates sequentially numbered codes (e.g. TRAY001, TRAY002). Skips duplicates. Lab-only operation.","tags":["Work Trays"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string","minLength":1,"maxLength":40},"quantity":{"type":"integer","minimum":1,"maximum":500},"color":{"type":"string"},"description":{"type":"string"}},"required":["pattern","quantity"]}}}},"responses":{"201":{"description":"Batch creation result with created count and duplicate errors","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"number"},"total":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"error":{"type":"string"}},"required":["code","error"]}}},"required":["created","total","errors"]}}}},"400":{"description":"Invalid request body"},"500":{"description":"Internal server error"}}}},"/work-trays/{id}":{"put":{"operationId":"updateWorkTray","description":"Update an existing work tray's code, color, description, active status, or sort order. Returns 409 if the new code conflicts with an existing tray. Lab-only operation.","tags":["Work Trays"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":50},"color":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"integer"}}}}}},"responses":{"200":{"description":"Successfully updated work tray","content":{"application/json":{"schema":{"type":"object","properties":{"tray":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"code":{"type":"string"},"color":{"type":["string","null"]},"description":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","code","description","isActive","sortOrder","createdAt","updatedAt"]}},"required":["tray"]}}}},"400":{"description":"Invalid request body"},"403":{"description":"Only labs can update work trays"},"404":{"description":"Tray not found"},"409":{"description":"A tray with this code already exists"},"500":{"description":"Internal server error"}}},"delete":{"operationId":"deleteWorkTray","description":"Soft-delete a work tray by marking it as inactive. Unassigns the tray from any active orders before deactivating. Lab-only operation.","tags":["Work Trays"],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Tray deactivated and unassigned from active orders","content":{"application/json":{"schema":{"type":"object","properties":{"tray":{"type":"object","properties":{"id":{"type":"string"},"labId":{"type":"string"},"code":{"type":"string"},"color":{"type":["string","null"]},"description":{"type":["string","null"]},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","labId","code","description","isActive","sortOrder","createdAt","updatedAt"]},"message":{"type":"string"}},"required":["tray","message"]}}}},"403":{"description":"Only labs can delete work trays"},"404":{"description":"Tray not found"},"500":{"description":"Internal server error"}}}}},"webhooks":{},"tags":[{"name":"Files"},{"name":"Orders"},{"name":"Quality Control"},{"name":"Shipments"},{"name":"Order Tags"},{"name":"Order Filter Presets"},{"name":"Patients"},{"name":"Partnerships"},{"name":"Partnership Payment Settings"},{"name":"Practice Groups"},{"name":"Organization Onboarding"},{"name":"Organization"},{"name":"Organization Settings"},{"name":"Team"},{"name":"Users"},{"name":"Admin"},{"name":"Conversations"},{"name":"Notifications"},{"name":"Push Subscriptions"},{"name":"Company Settings"},{"name":"Document Settings"},{"name":"Hold Reasons"},{"name":"QC Checklist"},{"name":"QC Settings"},{"name":"Receiving"},{"name":"Remake Reasons"},{"name":"Sticker Templates"},{"name":"Pickup Requests"},{"name":"Email Templates"},{"name":"Scanner Settings"},{"name":"Scanner Connections"},{"name":"WhatsApp Templates"},{"name":"Scan Inbox"},{"name":"Integrations"},{"name":"Invoices"},{"name":"Credit Notes"},{"name":"Statements"},{"name":"Consolidations"},{"name":"Transactions"},{"name":"Overpayments"},{"name":"Finance Settings"},{"name":"Finances"},{"name":"Clinic Finances"},{"name":"Products"},{"name":"Categories"},{"name":"Bundles"},{"name":"Materials"},{"name":"File Requirements"},{"name":"Shade Systems"},{"name":"Implant Systems"},{"name":"Components"},{"name":"Treatment Phases"},{"name":"Standards"},{"name":"Price Lists"},{"name":"Production"},{"name":"Inventory"},{"name":"Stripe Connect"},{"name":"Checkout"},{"name":"Clinic Payment Methods"},{"name":"Dashboard"},{"name":"Calendar"},{"name":"Reports"},{"name":"Export"},{"name":"Import"},{"name":"Data Bundle"},{"name":"AI"},{"name":"Search"},{"name":"Download"},{"name":"Clinic"},{"name":"Clients"},{"name":"Automations"},{"name":"Archive"},{"name":"Forms"},{"name":"Location Routes"},{"name":"Clinic Locations"},{"name":"Webhooks"},{"name":"Work Trays"}]}