Recipes
App API recipe browsing, favouriting, rating, and cook tracking
Recipes
These endpoints live on the App API. The standalone Food API exposes /api/v1/recipes for the catalog service.
GET /api/recipes
Search and browse the recipe catalog.
| Method | Path | Auth | Tier |
|---|---|---|---|
| GET | /api/recipes | Optional JWT | Free |
Query parameters
| Parameter | Type | Description |
|---|---|---|
q | string | Full-text search query |
category | string | Filter by cuisine (e.g. italian, asian) |
difficulty | string | easy | medium | hard |
max_time | integer | Max total time in minutes |
dietary | string | Dietary filter (e.g. vegetarian, vegan, gluten_free) |
limit | integer | Max results (default: 20) |
offset | integer | Pagination offset |
Response 200 OK
{
"items": [
{
"id": "uuid",
"title": "Pasta Carbonara",
"description": "Classic Roman pasta dish",
"cuisine": "italian",
"difficulty": "medium",
"prep_time": 10,
"cook_time": 20,
"servings": 4,
"calories": 520,
"is_favourite": false,
"rating_avg": 4.3,
"rating_count": 12,
"image_url": "https://..."
}
],
"total": 150,
"limit": 20,
"offset": 0
}GET /api/recipes/:id
Get full recipe details including ingredients and steps.
| Method | Path | Auth | Tier |
|---|---|---|---|
| GET | /api/recipes/:id | Optional JWT | Free |
Response 200 OK
{
"id": "uuid",
"title": "Pasta Carbonara",
"description": "Classic Roman pasta dish",
"cuisine": "italian",
"difficulty": "medium",
"prep_time": 10,
"cook_time": 20,
"servings": 4,
"is_favourite": false,
"rating_avg": 4.3,
"rating_count": 12,
"ingredients": [
{
"ingredient_id": 1,
"name": "Spaghetti",
"quantity": 400,
"unit": "g"
}
],
"steps": [
{
"step_number": 1,
"instruction": "Boil salted water and cook spaghetti until al dente."
}
],
"nutrition": {
"calories": 520,
"protein": 22,
"carbs": 68,
"fat": 18,
"fiber": 3
},
"images": ["https://..."]
}POST /api/recipes
Create a new recipe. Pro or Family tier required.
| Method | Path | Auth | Tier |
|---|---|---|---|
| POST | /api/recipes | JWT Bearer | Pro |
Request body
{
"title": "My Special Pasta",
"description": "A family favourite",
"cuisine": "italian",
"difficulty": "easy",
"prep_time": 15,
"cook_time": 30,
"servings": 4,
"ingredients": [
{ "ingredient_id": 1, "quantity": 400, "unit": "g" }
],
"steps": [
{ "step_number": 1, "instruction": "Cook pasta." }
]
}Response 201 Created
Returns the created recipe object with id.
PUT /api/recipes/:id
Update a recipe. Only the recipe's owner or an admin may update.
| Method | Path | Auth | Tier |
|---|---|---|---|
| PUT | /api/recipes/:id | JWT Bearer | Pro |
Same body as POST. Returns the updated recipe.
DELETE /api/recipes/:id
Delete a recipe. Only the owner or admin may delete.
| Method | Path | Auth | Tier |
|---|---|---|---|
| DELETE | /api/recipes/:id | JWT Bearer | Pro |
Response 204 No Content
POST /api/recipes/:id/favourite
Bookmark a recipe.
| Method | Path | Auth | Tier |
|---|---|---|---|
| POST | /api/recipes/:id/favourite | JWT Bearer | Free |
Response 200 OK
{ "is_favourite": true }DELETE /api/recipes/:id/favourite
Remove a recipe bookmark.
| Method | Path | Auth | Tier |
|---|---|---|---|
| DELETE | /api/recipes/:id/favourite | JWT Bearer | Free |
Response 200 OK
{ "is_favourite": false }POST /api/recipes/:id/rate
Rate a recipe (1–5 stars).
| Method | Path | Auth | Tier |
|---|---|---|---|
| POST | /api/recipes/:id/rate | JWT Bearer | Free |
Request body
{
"rating": 5,
"notes": "Absolutely delicious!"
}Response 200 OK
Returns the updated aggregate rating.
POST /api/recipes/:id/cook
Record that the user cooked this recipe. Triggers inventory deduction.
| Method | Path | Auth | Tier |
|---|---|---|---|
| POST | /api/recipes/:id/cook | JWT Bearer | Free |
Request body
{
"servings": 2
}Response 200 OK
{
"cooked_at": "2024-01-20T18:30:00Z",
"inventory_updated": true,
"ingredients_deducted": 4
}POST /api/recipes/generate
Generate a new recipe draft with AI. Pro tier required.
| Method | Path | Auth | Tier |
|---|---|---|---|
| POST | /api/recipes/generate | JWT Bearer | Pro |
Request body
{
"prompt": "high-protein breakfast with eggs",
"use_pantry": true,
"cuisine_hint": "Italian",
"max_minutes": 30
}| Field | Type | Notes |
|---|---|---|
prompt | string | Free-text recipe request from the user |
use_pantry | boolean | Prefer pantry ingredients when generating |
cuisine_hint | string | null | Optional cuisine/style steering |
max_minutes | integer | null | Optional total time cap |
Response 200 OK
{
"name": "Frittata al Parmigiano",
"description": "A fluffy baked egg dish...",
"cuisine": "Italian",
"difficulty": "easy",
"prep_minutes": 10,
"cook_minutes": 20,
"servings": 2,
"ingredients": [
{ "name": "eggs", "quantity": 4.0, "unit": "pieces", "is_pantry_item": true },
{ "name": "parmesan", "quantity": 50.0, "unit": "g", "is_pantry_item": false }
],
"steps": ["Preheat oven to 180°C", "Beat eggs with seasoning", "..."],
"macros_per_serving": {
"calories": 320,
"protein_g": 24,
"carbs_g": 4,
"fat_g": 22,
"fiber_g": 0
},
"tags": ["high-protein", "breakfast", "gluten-free"],
"score": {
"overall": 0.88,
"palatability": 0.91,
"nutrition_balance": 0.84,
"preference_match": 0.89,
"palatability_reason": "Well-balanced flavors with good protein content",
"iterations": 2
}
}Score object
| Field | Meaning |
|---|---|
overall | Best combined score chosen by the refinement loop |
palatability | How appealing the recipe is expected to be to eat |
nutrition_balance | Macro and nutrition quality signal |
preference_match | Fit against dietary preferences and user context |
palatability_reason | Short explanation for the taste/appeal score |
iterations | How many generate/score/refine passes were needed |
The server runs a generate → score → refine loop for up to 3 iterations and returns the best result. When use_pantry is true, pantry items are preferred and matching ingredients are flagged with is_pantry_item: true.