{"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://www.revolio.io","description":"Production"}],"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)","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","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","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","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","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","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","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","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","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","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","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}":{"get":{"summary":"Get repair details","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","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","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","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","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","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","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","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","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/v1/customers":{"get":{"summary":"List customers","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","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","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","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","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","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","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","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","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","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","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","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","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","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","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"}}}}}}}}