{"openapi":"3.0.3","info":{"title":"Recipe Box API","version":"1.0.0","description":"Read-only REST API over a catalog of 100 sample recipes. All responses are JSON and CORS-enabled, so the API can be called directly from a browser or mobile app. The spec is generated from the server's zod schemas, so it never drifts from the implementation."},"servers":[{"url":"https://178.105.233.80.nip.io","description":"Production"}],"tags":[{"name":"Recipes","description":"Browse and fetch recipes"},{"name":"Influencers","description":"Creators promoting recipes"},{"name":"Metadata","description":"Filter facets"},{"name":"Auth","description":"Anonymous user registration"},{"name":"Favorites","description":"Per-user saved recipes (bearer auth)"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Recipe":{"type":"object","properties":{"id":{"type":"integer","example":84},"name":{"type":"string","example":"Grilled Chicken Bowl"},"cuisine":{"type":"string","example":"Italian"},"category":{"type":"string","example":"Soup"},"difficulty":{"type":"string","enum":["easy","medium","hard"],"example":"easy"},"prep_min":{"type":"integer","example":17},"cook_min":{"type":"integer","example":38},"servings":{"type":"integer","example":1},"calories":{"type":"integer","example":688,"description":"Per serving"},"is_vegetarian":{"type":"integer","example":0,"description":"1 = vegetarian, 0 = not"},"image_url":{"type":"string","nullable":true,"example":"https://picsum.photos/seed/recipe84/600/400"}},"required":["id","name","cuisine","category","difficulty","prep_min","cook_min","servings","calories","is_vegetarian","image_url"]},"Ingredient":{"type":"object","properties":{"id":{"type":"integer","example":461},"recipe_id":{"type":"integer","example":84},"name":{"type":"string","example":"olive oil"},"quantity":{"type":"string","example":"3 cloves"}},"required":["id","recipe_id","name","quantity"]},"RecipeDetail":{"allOf":[{"$ref":"#/components/schemas/Recipe"},{"type":"object","properties":{"ingredients":{"type":"array","items":{"$ref":"#/components/schemas/Ingredient"}}},"required":["ingredients"]}]},"RecipeList":{"type":"object","properties":{"total":{"type":"integer","example":100,"description":"Total matches before pagination"},"count":{"type":"integer","example":20,"description":"Number returned in this page"},"limit":{"type":"integer","example":20},"offset":{"type":"integer","example":0},"recipes":{"type":"array","items":{"$ref":"#/components/schemas/Recipe"}}},"required":["total","count","limit","offset","recipes"]},"Influencer":{"type":"object","properties":{"id":{"type":"integer","example":1},"name":{"type":"string","example":"Mia Rivera"},"handle":{"type":"string","example":"miacooks"},"platform":{"type":"string","enum":["YouTube","TikTok","Instagram"],"example":"YouTube"},"followers":{"type":"integer","example":1320000},"avatar_url":{"type":"string","example":"https://i.pravatar.cc/200?img=5"},"video_url":{"type":"string","example":"https://www.youtube.com/watch?v=demo0001"},"video_title":{"type":"string","example":"I tried this Grilled Chicken Bowl 3 ways"},"recipe_id":{"type":"integer","example":12},"recipe_name":{"type":"string","example":"Guláš s knedlíkem"},"video_count":{"type":"integer","example":4}},"required":["id","name","handle","platform","followers","avatar_url","video_url","video_title","recipe_id","recipe_name","video_count"]},"InfluencerVideo":{"type":"object","properties":{"id":{"type":"integer","example":1},"influencer_id":{"type":"integer","example":1},"recipe_id":{"type":"integer","example":11},"video_url":{"type":"string","example":"https://www.youtube.com/watch?v=demo0100"},"video_title":{"type":"string","example":"Domácí Svíčková na smetaně"},"views":{"type":"integer","example":184000},"recipe_name":{"type":"string","example":"Svíčková na smetaně"},"recipe_image":{"type":"string","nullable":true},"recipe_category":{"type":"string","example":"Hlavní jídlo"}},"required":["id","influencer_id","recipe_id","video_url","video_title","views","recipe_name","recipe_image","recipe_category"]},"InfluencerDetail":{"allOf":[{"$ref":"#/components/schemas/Influencer"},{"type":"object","properties":{"videos":{"type":"array","items":{"$ref":"#/components/schemas/InfluencerVideo"}}},"required":["videos"]}]},"InfluencerList":{"type":"object","properties":{"count":{"type":"integer","example":10},"influencers":{"type":"array","items":{"$ref":"#/components/schemas/Influencer"}}},"required":["count","influencers"]},"Facets":{"type":"object","properties":{"cuisines":{"type":"array","items":{"type":"string"},"example":["French","Greek","Italian","Japanese"]},"categories":{"type":"array","items":{"type":"string"},"example":["Appetizer","Breakfast","Dessert","Main"]}},"required":["cuisines","categories"]},"Error":{"type":"object","properties":{"error":{"type":"string","example":"Recipe not found"}},"required":["error"]},"AuthToken":{"type":"object","properties":{"token":{"type":"string","example":"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b"},"user_id":{"type":"integer","example":1}},"required":["token","user_id"]},"FavoriteList":{"type":"object","properties":{"count":{"type":"integer","example":2},"recipes":{"type":"array","items":{"$ref":"#/components/schemas/Recipe"}}},"required":["count","recipes"]},"FavoriteResult":{"type":"object","properties":{"favorited":{"type":"boolean","example":true},"recipe_id":{"type":"integer","example":84}},"required":["favorited","recipe_id"]}},"parameters":{}},"paths":{"/api/recipes":{"get":{"tags":["Recipes"],"summary":"List recipes","description":"Returns a paginated list of recipes. All query parameters are optional and combine with AND semantics.","parameters":[{"schema":{"type":"string","example":"Beef"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","example":"Italian"},"required":false,"name":"cuisine","in":"query"},{"schema":{"type":"string","example":"Main"},"required":false,"name":"category","in":"query"},{"schema":{"type":"string","enum":["1","true"],"description":"Return only vegetarian recipes"},"required":false,"description":"Return only vegetarian recipes","name":"vegetarian","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of recipes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipeList"}}}}}}},"/api/recipes/random":{"get":{"tags":["Recipes"],"summary":"Get a random recipe","description":"Returns one randomly selected recipe, including its ingredients.","responses":{"200":{"description":"A random recipe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipeDetail"}}}},"404":{"description":"No recipes available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/recipes/{id}":{"get":{"tags":["Recipes"],"summary":"Get a recipe by id","description":"Returns a single recipe and its full ingredient list.","parameters":[{"schema":{"type":"integer","minimum":1,"example":84},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The requested recipe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipeDetail"}}}},"400":{"description":"Invalid id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Recipe not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/facets":{"get":{"tags":["Metadata"],"summary":"List filter facets","description":"Returns the distinct cuisines and categories present in the catalog, for building filter UIs.","responses":{"200":{"description":"Available facets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Facets"}}}}}}},"/api/influencers":{"get":{"tags":["Influencers"],"summary":"List influencers","description":"Returns the creators promoting recipes, each linked to the recipe they feature, ordered by follower count.","responses":{"200":{"description":"Influencers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InfluencerList"}}}}}}},"/api/influencers/{id}":{"get":{"tags":["Influencers"],"summary":"Get an influencer","description":"Returns one influencer and all the recipe videos they've made.","parameters":[{"schema":{"type":"integer","minimum":1,"example":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Influencer with videos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InfluencerDetail"}}}},"404":{"description":"Influencer not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/auth/register":{"post":{"tags":["Auth"],"summary":"Register an anonymous user","description":"Creates an anonymous user and returns a bearer token. Send this token as `Authorization: Bearer <token>` on favorites requests.","responses":{"200":{"description":"A new bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthToken"}}}}}}},"/api/favorites":{"get":{"tags":["Favorites"],"summary":"List favorites","description":"Returns the authenticated user's favorite recipes.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The user's favorites.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteList"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/favorites/{id}":{"put":{"tags":["Favorites"],"summary":"Add a favorite","description":"Adds the given recipe to the authenticated user's favorites.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"example":84},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Recipe favorited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Recipe not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Favorites"],"summary":"Remove a favorite","description":"Removes the given recipe from the authenticated user's favorites.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"example":84},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Recipe un-favorited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}