Skip to content

Recipes

Add, import, list, edit, delete recipes, and read cooking activity.

Updated View as Markdown

See Library & Recipes and Import from a URL for what these operations do in the UI. A recipe’s id is a slug derived from its title (deduplicated with a numeric suffix on collision), not a UUID.

GET /api/recipes

GETAny session

Lists every recipe in the library — the shared record only, with no per-profile rating, favorite, or cooked status attached (that state lives separately; see PATCH /api/recipes/:id below).

Response: { "recipes": RecipeRecord[] }

POST /api/recipes

POSTAny session

Creates a recipe by hand. multipart/form-data:

Field Type Required Notes
title string Yes
source string No Author or site name
sourceUrl string No
description string No
servings number No
prepMinutes number No
cookMinutes number No
difficulty "easy" | "medium" | "hard" No
cuisine string No
tags JSON string No JSON array of strings
ingredients JSON string No JSON array of { quantity?, unit?, name, note? }
steps JSON string No JSON array of { id?, text }
image file No Hero photo; rejected with 413 past the server’s upload size limit — see Settings
step-image-<stepId> file No One field per step that has a photo, keyed by that step’s id in the steps JSON

Response: 201 with { "recipe": RecipeRecord }, or 400 if title is missing, 413 if a photo is too large.

POST /api/recipes/import

POSTAny session

Imports a recipe from a URL — see Import from a URL for how the scraper works.

Request body: { "url": string }

Response: 201 with { "recipe": RecipeRecord }, 400 if url is missing or not http/https, or 422 if the page has no scrapeable recipe.

POST /api/recipes/:id/edit

POSTAny session

Updates an existing recipe. Same fields as POST /api/recipes above. Steps whose photo isn’t being replaced should echo back their current hasImage/imageExt in the steps JSON — the server diffs against the existing record and deletes any now-orphaned step photos.

Response: { "recipe": RecipeRecord }, or 404 if the recipe doesn’t exist, 400 if title is missing, 413 if a photo is too large.

PATCH /api/recipes/:id

PATCHAny session

Updates the active profile’s state for one recipe — rating, favorite, and cooked status all live per-profile, not on the shared recipe record.

Request body — all fields optional:

Field Type Notes
rating integer 0–5 0 clears the rating
favorite boolean
cooked boolean Setting it true also stamps lastCookedAt

Response: { "recipe": Recipe } — the recipe merged with the active profile’s state.

DELETE /api/recipes/:id

DELETEAdmin only

Deletes the recipe’s files from storage, removes it from the index, and clears every profile’s state for it.

Response: { "ok": true }

GET /api/recipes/:id/activity

GETAny session

Lists other profiles (excluding the active one) that have cooked, rated, or favorited this recipe — powers the “Cooking Activity” panel on a shared install.

Response: { "activity": [{ "profileId": string, "name": string, "color": string, "cooked": boolean, "rating"?: number }] }

Navigation

Type to search…

↑↓ navigate↵ selectEsc close