{"openapi":"3.0.3","info":{"title":"Revolio Public API","version":"v1","description":"Public API for Revolio repair shops. Endpoints are derived from the current route handlers and support API key authentication via X-API-Key or Authorization: Bearer, except WooCommerce connect which uses shop credentials."},"servers":[{"url":"https://api.revolio.io","description":"Canonical public API base URL"},{"url":"https://www.revolio.io","description":"Legacy compatible base URL"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"paths":{"/api/public/v1/health":{"get":{"summary":"Health check (authenticated)","operationId":"health-get","description":"Validates the API key and returns the authenticated shop context and granted scopes.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"status":"ok","version":"v1","time":"2026-04-05T12:00:00.000Z","context":{"repairShopId":"shop_123","keyId":"key_123","keyName":"WooCommerce sync","scopes":["repairs.read","repairs.write","products.write"]}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}}}},"/api/public/v1/me":{"get":{"summary":"Get authenticated shop profile summary","operationId":"me-get","description":"Returns the authenticated shop profile metadata currently exposed by the API. Required scope: parts.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"countryCode":"PT","country":"Portugal","companyName":"Loja Exemplo","city":"Lisboa","postalCode":"1000-001","street":"Rua da Revolio 10"}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}}}},"/api/public/v1/catalog/brands":{"get":{"summary":"List brands","operationId":"catalog-brands-get","description":"Returns active brands available for repairs and catalog filtering. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"brand_apple","name":"Apple","logo":"https://cdn.revolio.io/brands/apple.svg"}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/catalog/categories":{"get":{"summary":"List categories","operationId":"catalog-categories-get","description":"Returns active device categories available for repairs and catalog filtering. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"cat_phone","name":"Smartphones","description":"Telemoveis e smartphones","image":null}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/catalog/component-types":{"get":{"summary":"List component types","operationId":"catalog-component-types-get","description":"Returns component types used by spare parts filtering. Required scope: parts.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"component_screen","name":"Ecra","icon":"monitor","color":"#0F766E"}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/catalog/device-models":{"get":{"summary":"List device models","operationId":"catalog-device-models-get","description":"Returns active device models filtered by brand, category, id, or free-text search. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"dm_iphone_14_pro","name":"iPhone 14 Pro","releaseYear":2022,"image":"https://cdn.revolio.io/device-models/iphone-14-pro.png","brand":{"id":"brand_apple","name":"Apple"},"category":{"id":"cat_phone","name":"Smartphones"}}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"id","in":"query","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"brandId","in":"query","required":false,"schema":{"type":"string"}},{"name":"categoryId","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/catalog/problem-types":{"get":{"summary":"List problem types","operationId":"catalog-problem-types-get","description":"Returns active fault types used by repairs and quotes. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"problem_screen","name":"Ecra partido","description":"Substituicao de ecra ou vidro","icon":"smartphone","categoryIds":["cat_phone"],"mappingTags":["screen","display"]}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"id","in":"query","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/catalog/repair-statuses":{"get":{"summary":"List repair statuses","operationId":"catalog-repair-statuses-get","description":"Returns the full RepairStatus enum currently supported by the backend. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":["PENDING_PAYMENT","PAYMENT_FAILED","PAID","CONFIRMED","IN_REPAIR","COMPLETED","DELIVERED"]}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}}}},"/api/public/v1/repairs":{"get":{"summary":"List repairs","operationId":"repairs-list-get","description":"Returns paginated repairs for the authenticated repair shop. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"repair_123","trackingCode":"REP-ABC123","status":"PENDING_PAYMENT","customerName":"Joao Silva","customerPhone":"+351912345678","deviceDescription":"iPhone 14 Pro","description":"Ecra partido","estimatedPrice":129.9,"finalPrice":129.9,"createdAt":"2026-04-05T12:00:00.000Z","updatedAt":"2026-04-05T12:00:00.000Z","completedDate":null,"deliveredAt":null,"device":{"id":"dm_iphone_14_pro","name":"iPhone 14 Pro","brand":{"id":"brand_apple","name":"Apple"}},"problemType":{"id":"problem_screen","name":"Ecra partido"}}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"trackingCode","in":"query","required":false,"schema":{"type":"string"}}]},"post":{"summary":"Create repair","operationId":"repairs-create-post","description":"Creates a repair for the authenticated shop using the validated repair payload. Required scope: repairs.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"repairId":"repair_123","trackingCode":"REP-ABC123","total":129.9,"currency":"EUR","status":"PENDING_PAYMENT"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["customer","services","deliveryMethod"],"properties":{"customer":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"nif":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":"string"}}},"device":{"type":"object","properties":{"deviceModelId":{"type":"string"},"description":{"type":"string"}}},"deviceModelId":{"type":"string"},"imei":{"type":"string"},"deviceUnlockType":{"type":"string","enum":["NONE","PIN","PATTERN"]},"deviceUnlockPin":{"type":"string"},"deviceUnlockPattern":{"type":"string"},"services":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"object","required":["problemTypeId","description","price"],"properties":{"problemTypeId":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"estimatedTime":{"type":"integer"},"selectedVariantId":{"type":"string","nullable":true},"selectedVariantName":{"type":"string","nullable":true},"selectedVariantPrice":{"type":"number","nullable":true},"selectedSparePartId":{"type":"string","nullable":true},"selectedSparePartName":{"type":"string","nullable":true},"selectedSparePartSku":{"type":"string","nullable":true},"selectedSparePartPrice":{"type":"number","nullable":true},"selectedSparePartEan":{"type":"string","nullable":true},"selectedSupplierId":{"type":"string","nullable":true},"selectedSupplierProductId":{"type":"string","nullable":true}}}},"deliveryMethod":{"type":"string","enum":["STORE_PICKUP","DELIVERY","MAIL_SEND","COURIER_PICKUP","SHIPPING"]},"description":{"type":"string"},"problemImages":{"type":"array","items":{"type":"string","format":"uri"}},"external":{"type":"object","additionalProperties":true}}},"example":{"customer":{"name":"Joao Silva","email":"joao@example.com","phone":"+351912345678","city":"Lisboa","postalCode":"1000-001","country":"PT"},"device":{"deviceModelId":"dm_iphone_14_pro","description":"iPhone 14 Pro 128GB"},"imei":"490154203237518","services":[{"problemTypeId":"problem_screen","description":"Substituicao de ecra","price":129.9,"estimatedTime":60}],"deliveryMethod":"STORE_PICKUP","description":"Cliente deixou o equipamento no balcao","external":{"source":"woocommerce","orderId":"WC-1001"}}}}}}},"/api/public/v1/repairs/quote":{"post":{"summary":"Get repair quote","operationId":"repairs-quote-post","description":"Calculates pricing for one or more repair services using the pricing fallback chain configured in the backend. Required scope: pricing.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"currency":"EUR","total":129.9,"lines":[{"problemTypeId":"problem_screen","repairShopPriceId":"price_123","basePrice":129.9,"hasVariants":false,"variants":[],"selectedVariantId":null,"resolvedPrice":129.9,"estimatedTime":60,"warrantyMonths":6}]}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["services"],"properties":{"services":{"type":"array","minItems":1,"items":{"type":"object","required":["problemTypeId"],"properties":{"problemTypeId":{"type":"string"},"deviceModelId":{"type":"string"},"categoryId":{"type":"string"},"selectedVariantId":{"type":"string","nullable":true}}}}}},"example":{"services":[{"problemTypeId":"problem_screen","deviceModelId":"dm_iphone_14_pro"}]}}}}}},"/api/public/v1/repairs/{id}/quote-proposals":{"get":{"summary":"List quote proposals","operationId":"repair-quote-proposals-list-get","description":"Returns formal quote proposals (options and lines) for a shop-owned repair. Accepts repair id or trackingCode. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"proposal_123","status":"SENT","version":1,"title":"Ecrã iPhone 13","options":[{"id":"opt_1","title":"Original","totalPrice":89.9,"isRecommended":true}]}]}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]},"post":{"summary":"Create quote proposal","operationId":"repair-quote-proposals-create-post","description":"Creates a formal quote proposal with options and lines, moves the repair to QUOTE and emits the quote.created webhook. Required scope: repairs.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"proposal_123","status":"SENT","version":1,"title":"Ecrã iPhone 13","options":[{"id":"opt_1","title":"Original","totalPrice":89.9,"isRecommended":true}]}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["options"],"properties":{"title":{"type":"string"},"customerMessage":{"type":"string"},"internalNotes":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"options":{"type":"array","minItems":1,"items":{"type":"object","required":["title","lines"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"isRecommended":{"type":"boolean"},"lines":{"type":"array","minItems":1,"items":{"type":"object","required":["label","quantity","unitPrice"],"properties":{"type":{"type":"string","enum":["SERVICE","PART","LABOR","FEE","OTHER"]},"label":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"number"},"unitPrice":{"type":"number"},"vatRate":{"type":"number"},"isOptional":{"type":"boolean"}}}}}}}}},"example":{"title":"Ecrã iPhone 13","customerMessage":"Orçamento válido por 7 dias.","options":[{"title":"Original","isRecommended":true,"lines":[{"type":"PART","label":"Ecrã OLED","quantity":1,"unitPrice":65},{"type":"LABOR","label":"Mão de obra","quantity":1,"unitPrice":24.9}]}]}}}}}},"/api/public/v1/repairs/{id}":{"get":{"summary":"Get repair details","operationId":"repair-detail-get","description":"Returns the repair detail payload for a shop-owned repair. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"repair_123","trackingCode":"REP-ABC123","status":"PENDING_PAYMENT","priority":"NORMAL","customer":{"name":"Joao Silva","phone":"+351912345678","email":"joao@example.com"},"device":{"id":"dm_iphone_14_pro","name":"iPhone 14 Pro","brand":{"id":"brand_apple","name":"Apple"},"description":"iPhone 14 Pro 128GB"},"problemType":{"id":"problem_screen","name":"Ecra partido"},"description":"Cliente deixou o equipamento no balcao","internalNotes":null,"pricing":{"estimated":129.9,"final":129.9,"paid":0},"parts":[{"id":"repair_part_1","name":"Ecra OLED","quantity":1,"unitPrice":79.9,"totalPrice":79.9}],"dates":{"created":"2026-04-05T12:00:00.000Z","updated":"2026-04-05T12:00:00.000Z","estimatedCompletion":null,"completed":null,"delivered":null},"statusHistory":[{"id":"hist_1","status":"PENDING_PAYMENT","notes":"Created via Public API","timestamp":"2026-04-05T12:00:00.000Z"}]}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/public/v1/repairs/{id}/status":{"post":{"summary":"Update repair status","operationId":"repair-status-post","description":"Updates the repair status and optionally stores notes in status history. Required scope: repairs.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"repair_123","status":"IN_REPAIR"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["PENDING_PAYMENT","PAYMENT_FAILED","PAID","CONFIRMED","RECEIVED","DIAGNOSIS","WAITING_PARTS","IN_REPAIR","TESTING","COMPLETED","DELIVERED","CANCELLED","DISPUTE"]},"notes":{"type":"string","maxLength":500}}},"example":{"status":"IN_REPAIR","notes":"Equipamento em bancada"}}}}}},"/api/public/v1/repairs/{id}/items":{"get":{"summary":"List repair items","operationId":"repair-items-get","description":"Lists repair items attached to a shop-owned repair. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"repair_item_1","name":"Ecra OLED","description":"Ecra premium","quantity":1,"price":79.9,"createdAt":"2026-04-05T12:05:00.000Z","updatedAt":"2026-04-05T12:05:00.000Z","part":{"id":"part_1","sku":"SCR-IPH14PRO","name":"Ecra OLED"}}]}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/public/v1/repairs/{id}/services":{"get":{"summary":"List repair services","operationId":"repair-services-get","description":"Lists repair service lines attached to a shop-owned repair. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"service_1","description":"Substituicao de ecra","price":129.9,"estimatedTime":60,"sortOrder":0,"createdAt":"2026-04-05T12:00:00.000Z","updatedAt":"2026-04-05T12:00:00.000Z","problemType":{"id":"problem_screen","name":"Ecra partido"},"selectedVariant":null,"selectedSparePart":null,"supplier":null}]}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/public/v1/repairs/{id}/messages":{"get":{"summary":"List repair messages","operationId":"repair-messages-get","description":"Returns paginated messages for the repair thread. Required scope: messages.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"msg_1","message":"A reparacao ja entrou em bancada.","createdAt":"2026-04-05T13:00:00.000Z","updatedAt":"2026-04-05T13:00:00.000Z","sender":{"id":"shop_123","name":"Loja Exemplo","email":"loja@example.com","role":"REPAIR_SHOP"}}],"pagination":{"total":1,"limit":50,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"maximum":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}}]},"post":{"summary":"Send repair message","operationId":"repair-messages-post","description":"Creates a new message in the repair thread. Required scope: messages.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"msg_1","message":"A reparacao ja entrou em bancada.","createdAt":"2026-04-05T13:00:00.000Z","updatedAt":"2026-04-05T13:00:00.000Z"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string","minLength":1,"maxLength":4000}}},"example":{"message":"A reparacao ja entrou em bancada."}}}}}},"/api/public/v1/repairs/{id}/payments":{"get":{"summary":"List repair payments","operationId":"repair-payments-get","description":"Lists payment records for the repair. Required scope: payments.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"payment_1","amount":129.9,"currency":"eur","status":"COMPLETED","method":"woocommerce","paymentMethodType":"card","paymentMethodDetails":{"orderId":"WC-1001"},"platformFee":null,"shopAmount":null,"failureReason":null,"createdAt":"2026-04-05T14:00:00.000Z","updatedAt":"2026-04-05T14:00:00.000Z","releasedAt":null}]}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]},"post":{"summary":"Register repair payment","operationId":"repair-payments-post","description":"Creates a payment record for the repair and can transition the repair to PAID on completed payments. Required scope: payments.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"payment_1","amount":129.9,"currency":"eur","status":"COMPLETED","method":"woocommerce","createdAt":"2026-04-05T14:00:00.000Z","repair":{"id":"repair_123","status":"PAID"}}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount"],"properties":{"amount":{"type":"number"},"currency":{"type":"string","default":"EUR"},"status":{"type":"string","enum":["PENDING","COMPLETED","FAILED","REFUNDED","ESCROW","RELEASED"]},"method":{"type":"string"},"paymentMethodType":{"type":"string"},"details":{"type":"object","additionalProperties":true},"stripePaymentIntentId":{"type":"string"}}},"example":{"amount":129.9,"currency":"EUR","status":"COMPLETED","method":"woocommerce","paymentMethodType":"card","details":{"orderId":"WC-1001"}}}}}}},"/api/public/v1/repairs/{id}/events":{"get":{"summary":"List repair timeline events","operationId":"repair-events-get","description":"Returns repair status timeline events ordered by creation date. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"hist_1","type":"status_changed","status":"IN_REPAIR","notes":"Equipamento em bancada","updatedBy":"shop_123","updatedByType":"REPAIR_SHOP","employee":null,"createdAt":"2026-04-05T14:10:00.000Z"}]}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/public/mcp":{"post":{"summary":"MCP server (AI tools)","operationId":"mcp-server-post","description":"Stateless MCP Streamable-HTTP endpoint exposing 39 shop-scoped AI tools (pricing, quotes, tracking, WhatsApp, customers, catalog, repair detail, messages, stats, status updates, payments, spare parts, shipping quotes, uploads, diagnostics, invoices, WooCommerce status, shop inventory, movements, purchase orders, suppliers, storefront theme/content/sections/SEO). Send JSON-RPC 2.0 tools/list and tools/call. Requires the mcp:use scope; each tool enforces its own domain scope. Required scope: mcp:use.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"currency\":\"EUR\",\"total\":89.9}"}]}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"405":{"description":"Error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","id","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","integer"]},"method":{"type":"string","enum":["tools/list","tools/call","ping"]},"params":{"type":"object"}}},"example":{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"calculate_quote_price","arguments":{"services":[{"problemTypeId":"problem_screen"}]}}}}}}}},"/api/public/v1/customers":{"get":{"summary":"List customers","operationId":"customers-list-get","description":"Lists customers derived from repairs for the authenticated shop. Required scope: customers.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"customer_1","name":"Joao Silva","email":"joao@example.com","phone":"+351912345678","nif":"123456789","address":"Rua da Revolio 10","city":"Lisboa","postalCode":"1000-001","country":"PT","repairsCount":3,"lastRepairAt":"2026-04-05T12:00:00.000Z"}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/customers/{id}":{"get":{"summary":"Get customer details","operationId":"customers-detail-get","description":"Returns customer detail and recent repairs for a shop-owned customer. Required scope: customers.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"customer_1","name":"Joao Silva","email":"joao@example.com","phone":"+351912345678","nif":"123456789","address":"Rua da Revolio 10","city":"Lisboa","postalCode":"1000-001","country":"PT","repairsCount":3,"recentRepairs":[{"id":"repair_123","trackingCode":"REP-ABC123","status":"PENDING_PAYMENT","deviceDescription":"iPhone 14 Pro","estimatedPrice":129.9,"finalPrice":129.9,"createdAt":"2026-04-05T12:00:00.000Z"}],"createdAt":"2025-10-12T09:00:00.000Z","updatedAt":"2026-04-05T12:00:00.000Z"}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/public/v1/stats":{"get":{"summary":"Get basic shop stats","operationId":"stats-get","description":"Returns basic repair, customer, and revenue aggregates for the authenticated shop. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"repairs":{"total":340,"thisMonth":25,"lastMonth":21,"byStatus":{"pending":9,"inProgress":7,"completed":17}},"customers":{"total":210},"revenue":{"total":48210.5,"thisMonth":3290.2},"generatedAt":"2026-04-05T12:00:00.000Z"}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}}}},"/api/public/v1/analytics/repairs":{"get":{"summary":"Get repair analytics","operationId":"analytics-repairs-get","description":"Returns repair analytics aggregated for a given time range. Required scope: repairs.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"range":{"from":"2026-03-01T00:00:00.000Z","to":"2026-03-31T23:59:59.999Z"},"repairs":{"created":25,"completed":18,"byStatus":{"PAID":4,"IN_REPAIR":7,"COMPLETED":10},"avgCompletionDays":2.4},"customers":{"unique":21},"revenue":{"total":3290.2},"series":{"dailyCreatedRepairs":[{"date":"2026-03-01","count":2},{"date":"2026-03-02","count":1}]}}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}]}},"/api/public/v1/parts":{"get":{"summary":"List legacy parts","operationId":"parts-list-get","description":"Returns the legacy parts list for the authenticated shop. Required scope: parts.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"part_1","sku":"SCR-IPH14PRO","name":"Ecra OLED","description":"Ecra premium","category":"Displays","price":79.9,"stock":12,"images":["https://cdn.revolio.io/parts/scr-iph14pro.png"],"brand":{"id":"brand_apple","name":"Apple"},"device":{"id":"device_123","name":"iPhone 14 Pro"},"createdAt":"2026-04-05T12:00:00.000Z","updatedAt":"2026-04-05T12:00:00.000Z"}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"brandId","in":"query","required":false,"schema":{"type":"string"}},{"name":"deviceId","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/spare-parts":{"get":{"summary":"List spare parts with offer comparison","operationId":"spare-parts-list-get","description":"Returns the enriched spare parts catalog including offers and best price resolution. Required scope: parts.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"spare_part_1","sku":"SCR-IPH14PRO","ean":"1234567890123","name":"Ecra OLED","description":"Ecra premium","price":74.9,"originalPrice":79.9,"stock":12,"images":["https://cdn.revolio.io/parts/scr-iph14pro.png"],"availability":true,"deliveryTime":1,"category":{"id":"cat_phone","name":"Smartphones"},"brand":{"id":"brand_apple","name":"Apple","logo":"https://cdn.revolio.io/brands/apple.svg"},"device":{"id":"dm_iphone_14_pro","name":"iPhone 14 Pro","image":"https://cdn.revolio.io/device-models/iphone-14-pro.png"},"componentType":{"id":"component_screen","name":"Ecra"},"compatibility":["iPhone 14 Pro"],"createdAt":"2026-04-05T12:00:00.000Z","offers":[{"source":"SPARE_PART","partId":"spare_part_1","supplierName":"Revolio","isOfficialSupplier":true,"price":79.9,"stock":12,"availability":true,"deliveryTime":1},{"source":"SUPPLIER_PRODUCT","supplierProductId":"sup_prod_1","supplierId":"supplier_1","supplierName":"Distribuidor X","supplierLogo":null,"isOfficialSupplier":false,"price":74.9,"stock":5,"availability":true,"deliveryTime":2}]}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"brandId","in":"query","required":false,"schema":{"type":"string"}},{"name":"deviceId","in":"query","required":false,"schema":{"type":"string"}},{"name":"categoryId","in":"query","required":false,"schema":{"type":"string"}},{"name":"componentTypeId","in":"query","required":false,"schema":{"type":"string"}},{"name":"sortBy","in":"query","required":false,"schema":{"type":"string","enum":["newest","price_asc","price_desc","name"]}}]}},"/api/public/v1/spare-parts/order":{"post":{"summary":"Create spare parts order","operationId":"spare-parts-order-post","description":"Creates a spare parts order with stock validation, shipping calculation, and tax computation. Required scope: parts.read + orders.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":{"id":"sp_order_1","orderNumber":"SP1712323123ABCD","status":"PENDING","items":[{"id":"item_1","name":"Ecra OLED","sku":"SCR-IPH14PRO","quantity":1,"price":79.9,"totalPrice":79.9}],"shippingCost":5.5,"tax":19.66,"total":105.06,"currency":"EUR","createdAt":"2026-04-05T15:00:00.000Z"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["quantity"],"properties":{"partId":{"type":"string"},"supplierProductId":{"type":"string"},"quantity":{"type":"integer","minimum":1,"maximum":10000}}}},"notes":{"type":"string","maxLength":2000}}},"example":{"items":[{"partId":"spare_part_1","quantity":1}],"notes":"Pedido para bancada 3"}}}}}},"/api/public/v1/spare-parts/shipping":{"post":{"summary":"Calculate spare parts shipping","operationId":"spare-parts-shipping-post","description":"Calculates the shipping cost for a spare parts order payload. Required scope: parts.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"shippingCost":5.5,"breakdown":[{"supplierId":"REVOLIO","cost":5.5}]}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["quantity"],"properties":{"partId":{"type":"string"},"supplierProductId":{"type":"string"},"quantity":{"type":"integer","minimum":1,"maximum":10000}}}}}},"example":{"items":[{"partId":"spare_part_1","quantity":1}]}}}}}},"/api/public/v1/suppliers":{"get":{"summary":"List suppliers","operationId":"suppliers-get","description":"Returns suppliers associated with the authenticated repair shop. Required scope: suppliers.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"data":[{"id":"supplier_1","name":"Distribuidor X","email":"comercial@distribuidorx.pt","phone":"+351210000000","website":"https://distribuidorx.pt","taxId":"PT123456789","address":"Av. Central 20","city":"Porto","postalCode":"4000-100","country":"PT","contactPerson":"Ana Costa","paymentTerms":"30 dias","rating":4.8,"isReparia":false,"isActive":true,"createdAt":"2025-11-10T10:00:00.000Z","updatedAt":"2026-04-05T11:00:00.000Z"}],"pagination":{"total":1,"limit":20,"offset":0,"hasMore":false}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/public/v1/woocommerce/capabilities":{"get":{"summary":"Get WooCommerce capabilities","operationId":"woocommerce-capabilities-get","description":"Returns the current WooCommerce integration status and available companion apps. Required scope: products.read.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"repairShopId":"shop_123","siteUrl":"https://lojaexemplo.pt","integration":{"appId":"woocommerce","installationId":"install_1","status":"ACTIVE"},"apps":{"inventoryManager":true}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}}}},"/api/public/v1/woocommerce/connect":{"post":{"summary":"Connect WooCommerce installation","operationId":"woocommerce-connect-post","description":"Authenticates with email and password, creates or updates the WooCommerce installation, generates a public API key, and registers sync webhooks.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"installationId":"install_1","webhookSecret":"whsec_xxxxxxxxxxxxxxxxx","publicApiKey":{"token":"rvl_abcd_xxxxxxxxxxxxxxxxxxxxxx","keyName":"WooCommerce","scopes":["repairs.read","repairs.write","customers.read","products.write"]}}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"402":{"description":"Active subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Active subscription required"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"},"siteUrl":{"type":"string","format":"uri"},"webhookUrl":{"type":"string","format":"uri"},"installationName":{"type":"string","maxLength":100}}},"example":{"email":"owner@lojaexemplo.pt","password":"password-super-segura","siteUrl":"https://lojaexemplo.pt","installationName":"Woo principal"}}}}}},"/api/public/v1/woocommerce/customers/upsert":{"post":{"summary":"Upsert WooCommerce customer","operationId":"woocommerce-customers-upsert-post","description":"Upserts a WooCommerce customer into the Revolio customer base and customer link table. Required scope: customers.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"revolioCustomerId":"customer_1"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["siteUrl","email"],"properties":{"siteUrl":{"type":"string","format":"uri"},"wooCustomerId":{"type":"integer"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"phone":{"type":"string"},"nif":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"},"external":{"type":"object","additionalProperties":true}}},"example":{"siteUrl":"https://lojaexemplo.pt","wooCustomerId":501,"email":"cliente@example.com","name":"Joao Silva","phone":"+351912345678","city":"Lisboa","postalCode":"1000-001","country":"PT"}}}}}},"/api/public/v1/woocommerce/orders/upsert":{"post":{"summary":"Upsert WooCommerce order","operationId":"woocommerce-orders-upsert-post","description":"Upserts a WooCommerce order into POS order structures and sync links. Required scope: orders.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"posOrderId":"pos_order_1","status":"PAID"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["siteUrl","wooOrderId","items"],"properties":{"siteUrl":{"type":"string","format":"uri"},"wooOrderId":{"type":"integer"},"wooOrderNumber":{"type":"string"},"status":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"},"customer":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"nif":{"type":"string"}}},"currency":{"type":"string"},"subtotal":{"type":"number"},"taxAmount":{"type":"number"},"total":{"type":"number"},"items":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"object","required":["name","quantity","unitPrice"],"properties":{"name":{"type":"string"},"sku":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"number"},"discountAmount":{"type":"number"},"taxRate":{"type":"number"}}}},"external":{"type":"object","additionalProperties":true}}},"example":{"siteUrl":"https://lojaexemplo.pt","wooOrderId":1001,"wooOrderNumber":"1001","status":"processing","customer":{"name":"Joao Silva","email":"joao@example.com","phone":"+351912345678","nif":"123456789"},"currency":"EUR","items":[{"name":"Ecra OLED","sku":"SCR-IPH14PRO","quantity":1,"unitPrice":79.9,"taxRate":23}]}}}}}},"/api/public/v1/woocommerce/products/upsert":{"post":{"summary":"Upsert WooCommerce product","operationId":"woocommerce-products-upsert-post","description":"Upserts a WooCommerce product into inventory, marketplace, and stock sync structures. Required scope: products.write.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"data":{"inventoryProductId":"inventory_1","marketplaceProductId":"marketplace_1"}}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["siteUrl","wooProductId","name","salePrice","stock"],"properties":{"siteUrl":{"type":"string","format":"uri"},"wooProductId":{"type":"integer"},"sku":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"salePrice":{"type":"number"},"stock":{"type":"integer"},"images":{"type":"array","items":{"type":"string","format":"uri"}},"updatedAt":{"type":"string","format":"date-time"},"external":{"type":"object","additionalProperties":true}}},"example":{"siteUrl":"https://lojaexemplo.pt","wooProductId":2001,"sku":"SCR-IPH14PRO","name":"Ecra OLED","description":"Ecra premium","salePrice":79.9,"stock":12,"images":["https://lojaexemplo.pt/wp-content/uploads/scr-iph14pro.png"]}}}}}},"/api/public/v1/woocommerce/upgrade-scopes":{"post":{"summary":"Upgrade API key scopes for WooCommerce","operationId":"woocommerce-upgrade-scopes-post","description":"Adds missing WooCommerce scopes to an existing WooCommerce API key after validating shop credentials.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object"},"example":{"ok":true,"upgraded":true,"added":["orders.write","products.write"],"scopes":["repairs.read","repairs.write","orders.write","products.write"]}}}},"400":{"description":"Invalid payload or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid payload or invalid query parameters"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing or invalid API key"}}}},"402":{"description":"Active subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Active subscription required"}}}},"403":{"description":"API key scope is insufficient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"API key scope is insufficient"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Resource not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal server error"}}}}},"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password","apiKey"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"},"apiKey":{"type":"string"}}},"example":{"email":"owner@lojaexemplo.pt","password":"password-super-segura","apiKey":"rvl_abcd_xxxxxxxxxxxxxxxxxxxxxx"}}}}}}}}