{"openapi":"3.1.1","info":{"title":"OneLife Shop API","version":"1.0.0","summary":"Browse the catalogue, place an order, and track it.","description":"The public API behind the OneLife storefront. Money is never taken through this API: an order records what the shopper chose and the reference from a payment made on the provider's own page. Prices and stock are live; anything baked into the static storefront is identity only.","license":{"name":"Proprietary","identifier":"LicenseRef-Proprietary"}},"servers":[{"url":"https://onelife-backend.onrender.com","description":"Production"}],"tags":[{"name":"Catalogue","description":"What is for sale."},{"name":"Orders","description":"Placing an order and following it afterwards."},{"name":"Reviews","description":"What shoppers said."},{"name":"Shop","description":"How this particular shop is configured."}],"paths":{"/healthz":{"get":{"tags":["Shop"],"summary":"Liveness check","description":"Answers 200 while the database is reachable.","responses":{"200":{"description":"The service is up."},"503":{"description":"The service is up but the database is not."}}}},"/api/products":{"get":{"tags":["Catalogue"],"summary":"Every product on sale","responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}}}}}},"/api/products/{id}":{"get":{"tags":["Catalogue"],"summary":"One product, with its reviews","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Product id."}],"responses":{"200":{"description":"The product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"404":{"description":"No product with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/settings":{"get":{"tags":["Shop"],"summary":"Public shop settings","description":"Brand name, policy figures, payment methods, contact details and the written content of the information pages. A setting the owner has not filled in comes back empty rather than invented, and the storefront hides whatever is empty.","responses":{"200":{"description":"Settings as a flat object of strings.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/coupons/validate":{"get":{"tags":["Orders"],"summary":"Check a discount code before using it","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string"},"description":"The code as typed."}],"responses":{"200":{"description":"The code is usable, with the discount it applies."},"400":{"description":"The code is expired, used up, below its minimum spend, or not a code at all. The message says which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts from this address."}}}},"/api/orders":{"post":{"tags":["Orders"],"summary":"Place an order","description":"Reserves stock and records the order as unpaid. Send the same `idempotency_key` twice and the second call returns the first order rather than placing a second.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"}}}},"responses":{"201":{"description":"The order was placed. The response carries its order number."},"400":{"description":"A required field is missing, or a product is out of stock. The message names the product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many orders from this address in a short window."}}}},"/api/orders/track/{orderNumber}":{"get":{"tags":["Orders"],"summary":"Follow an order already placed","description":"The contact detail must match the one on the order. A wrong one answers 404 rather than saying the order exists, so an order number alone reveals nothing about the buyer.","parameters":[{"name":"orderNumber","in":"path","required":true,"schema":{"type":"string","examples":["OL-20260828-FE84EFA9"]}},{"name":"contact","in":"query","required":true,"schema":{"type":"string"},"description":"The phone number or email address given when ordering."}],"responses":{"200":{"description":"The order's status and contents."},"400":{"description":"No contact detail was supplied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No order matches that number and contact detail."},"429":{"description":"Too many attempts from this address."}}}},"/api/reviews":{"post":{"tags":["Reviews"],"summary":"Leave a review","description":"The verified badge is not something the caller can set. It is granted only when the email given has a paid order containing this product.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewRequest"}}}},"responses":{"201":{"description":"The review was recorded."},"400":{"description":"The name, rating or comment is missing, or the rating is outside 1–5.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many reviews from this address in a short window."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Used by the shop owner's admin panel and by signed-in shoppers. No endpoint described in this document requires it."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"A message written for a shopper to read."}},"required":["error"]},"Product":{"type":"object","description":"Money and stock are absent rather than zero when they are not known — a caller with no figure must say nothing rather than print a zero.","properties":{"id":{"type":"integer"},"sku":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"price_usd":{"type":"number"},"compare_at_price_usd":{"type":"number","description":"The price this is discounted from, when there is one."},"price_aed":{"type":"number"},"compare_at_price_aed":{"type":"number"},"images":{"type":"array","items":{"type":"string","format":"uri-reference"}},"stock":{"type":"integer","description":"Units on the shelf, less anything reserved by unpaid orders."},"variants":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}}},"specs":{"type":"object","additionalProperties":{"type":"string"}},"features":{"type":"array","items":{"type":"string"}},"category":{"type":"string"},"video_url":{"type":"string","format":"uri"},"box_contents":{"type":"string"},"care_instructions":{"type":"string"}},"required":["id","sku","title","description","images"]},"OrderItem":{"type":"object","properties":{"product_id":{"type":"integer"},"title":{"type":"string"},"quantity":{"type":"integer","minimum":1},"variant":{"type":"string"},"price":{"type":"number"}},"required":["product_id","quantity"]},"OrderRequest":{"type":"object","properties":{"customer_name":{"type":"string"},"customer_email":{"type":"string","format":"email","description":"Optional. Left empty rather than filled in with a placeholder."},"customer_phone":{"type":"string"},"customer_address":{"type":"string"},"currency":{"type":"string","enum":["USD","AED"]},"coupon_code":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/OrderItem"},"minItems":1},"payment_method":{"type":"string"},"payment_reference":{"type":"string","description":"The reference the payment provider gave the shopper."},"idempotency_key":{"type":"string","description":"Send the same key to retry safely."}},"required":["customer_name","customer_phone","customer_address","items"]},"ReviewRequest":{"type":"object","properties":{"product_id":{"type":"integer"},"reviewer_name":{"type":"string"},"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string"},"email":{"type":"string","format":"email","description":"Only used to decide the verified badge; never published."}},"required":["product_id","reviewer_name","rating","comment"]}}}}