Developer API

Build with The Saucery

Wire the family shopping list into whatever else you run. Create an API key in The Saucery, then use the endpoints below.

Quick start

Three steps

  1. Create an API key

    In The Saucery, open Settings → API Keys. Only family owners can create one.

  2. Choose your scopes

    Grant only what the integration needs. Start with shopping-list:read.

  3. Make requests

    Pass the key as a Bearer token. The reference below lists every endpoint.

Reference

The Saucery Developer API lets you manage shopping lists programmatically.

Authentication

All requests require a Bearer token. Create an API key in the app under Settings → API Keys (family owner only).

Authorization: Bearer sc_key_xxxxxxxxxxxxxxxx

Scopes

Keys can be scoped to limit access:

Scope Description
shopping-list:read Read shopping list and items
shopping-list:write Add, check, and remove shopping list items

The endpoints

6 endpoints

Every endpoint in the The Saucery developer API
Method Endpoint
GET /meal-plan Get the meal plan for the current (or specified) week
GET /shopping-list Get the current shopping list
GET /shopping-list/items/{item} Get a single shopping list item
DELETE /shopping-list/items/{item} Remove an item from the shopping list
POST /shopping-list/items Add an item to the shopping list
POST /shopping-list/items/{item}/check Toggle an item's checked status

Base URL https://thesaucery.nz/api/v1. Version 1.0.0.

Download the OpenAPI document

Meal plan

GET /meal-plan

Get the meal plan for the current (or specified) week

Example request

curl -X GET https://thesaucery.nz/api/v1/meal-plan \
  -H "Authorization: Bearer sc_key_..."

Response 200 · OK

Response body for Get the meal plan for the current (or specified) week
Field Type
data object
data.id string
data.family_id any
data.week_start_date string
data.entries object[]optional
data.entries[].id string
data.entries[].date string
data.entries[].meal_type string
data.entries[].recipe objectoptional
data.entries[].recipe.id string
data.entries[].recipe.family_id any
data.entries[].recipe.web_url string
data.entries[].recipe.title string
data.entries[].recipe.description string or null
data.entries[].recipe.cuisine string or null
data.entries[].recipe.tags any
data.entries[].recipe.source_type string
data.entries[].recipe.source_url string or null
data.entries[].recipe.source_name string or null
data.entries[].recipe.prep_time_minutes integer or null
data.entries[].recipe.cook_time_minutes integer or null
data.entries[].recipe.total_time_minutes integer or null
data.entries[].recipe.servings integer
data.entries[].recipe.photos string[]
data.entries[].recipe.is_stylizing_hero boolean
data.entries[].recipe.sketch_url string or null
data.entries[].recipe.is_sketching boolean
data.entries[].recipe.notes string or null
data.entries[].recipe.image_prompt string or null
data.entries[].recipe.visibility string
data.entries[].recipe.is_favourite boolean
data.entries[].recipe.needs_review boolean
data.entries[].recipe.ingredients object[]optional
data.entries[].recipe.steps object[]optional
data.entries[].recipe.last_cooked_at stringoptional
data.entries[].recipe.times_cooked integeroptional
data.entries[].recipe.comments object[]optional
data.entries[].recipe.created_by objectoptional
data.entries[].recipe.lab_items object[]optional
data.entries[].recipe.created_at string or null
data.entries[].recipe.updated_at string or null
data.entries[].recipe.is_starter True for one of the sample recipes every new family's book starts with, which the family did not add. Always present. boolean
data.entries[].recipe.byline Where the recipe came from when it is not the family's own, ready to show under the title, e.g. "From The Saucery". Null for the family's own recipes. string or null
data.entries[].recipe.scales True when a planned meal of this recipe is cooked for the household and starts at the family's default servings. False for a batch, such as a cake or a loaf, which is planned at the recipe's own servings. Always present. boolean
data.entries[].recipe.shared_by_name The name of the person who sent the link this recipe was saved from, e.g. "Grace". Null for a recipe that was not saved from a share link. Separate from source_name, which says where the recipe originally came from. string or null
data.entries[].recipe.grocery_specials string[]
data.entries[].recipe_id string
data.entries[].recipe_link_source 'user' if someone picked the recipe, 'auto' if we inferred it from the label they typed. Clients can use it to offer an "not this one?" escape hatch on a link they didn't ask for; the label stays the display string either way. string or null
data.entries[].label string or null
data.entries[].servings integer or null
data.entries[].postponed_from_entry_id Kept for clients shipped before the day a postponed meal left was freed. Always null now — read postponed_from_date. string
data.entries[].postponed_from_date The day this meal was first planned on, if it was postponed from there. Later moves keep it, and it clears when the meal goes back to that day or a different meal takes its place. Its ingredients were bought for the original week, so it is deliberately absent from this week's shopping list. string
data.entries[].ingredients_already_purchased boolean
data.entries[].cooked_at string
data.entries[].cook Who is cooking this meal, or null when nobody has been named. It moves with the meal: a move, swap or postpone takes it along, and a different meal on the same night keeps it. Set it with PUT /meal-plan/entry/{id}/cook. `user_id` is the member's user id (equal to `id`) when the cook is in the family, or null for someone without the app, whose `id` is one of the family's `cooks`. `pending` is true while that person is invited and has not joined. any
data.entries[].cook_user_id The cook's user id (the same id as the family's members), or null, including when the cook is someone without the app. string or null
data.created_at string or null
data.updated_at string or null

Shopping list

GET /shopping-list

Get the current shopping list

Example request

curl -X GET https://thesaucery.nz/api/v1/shopping-list \
  -H "Authorization: Bearer sc_key_..."

Response 200 · OK

Response body for Get the current shopping list
Field Type
data object
data.id string
data.family_id any
data.week_start_date string
data.items object[]optional
data.items[].id string
data.items[].name string
data.items[].quantity What to buy: a count is whole and a weight or volume is rounded up to what a shelf sells. A line the family typed in is returned as typed. string
data.items[].unit string
data.items[].display_quantity The amount as it reads before the name, in the unit it is bought in: "2.1 l", "5 tbsp", or "about 5" for a weight of something bought by the piece. Null when the line has no amount. string
data.items[].quantity_note A second line about the amount, or null: the weight behind an "about" count ("540 g altogether"), or "You probably have this in the pantry" when probably_at_home is true. string
data.items[].probably_at_home True for a spoonful of something from the pantry aisles, or a splash of oil or sauce, that most kitchens already have. The line stays on the list. string
data.items[].group string or null
data.items[].is_checked boolean
data.items[].checked_by string
data.items[].checked_by_name The name of the family member who ticked the line, or null when it is not ticked or the tick predates the record. string or null
data.items[].checked_at When the line was ticked, ISO 8601, or null. string or null
data.items[].added_by Who put the line on the list, {id, name}: the person who typed it, asked the assistant for it, or added the plan, recipe or staples it came from. Null for lines added before this was recorded and for lines added through an integration key. object or null
data.items[].added_by.id string
data.items[].added_by.name string
data.items[].recipe_id string
data.items[].staple_id string
data.items[].is_manual boolean
data.items[].is_skipped boolean
data.items[].source_type enum
data.items[].recipe_names objectoptional
data.items[].contributions Per-recipe prep sub-lines, e.g. {recipe_name: "Roast Chicken Dinner", detail: "sliced, raw", date: "2026-10-12"}. Lets clients render the base name as the row with each recipe's specifics underneath. date is the night the recipe is planned for (Y-m-d), the earliest when the week has it twice, and null for a recipe added from its own page. object[]optional
data.items[].contributions[].recipe_id string
data.items[].contributions[].recipe_name string
data.items[].contributions[].detail string
data.items[].contributions[].date string or null
data.items[].is_on_special boolean
data.items[].price string or null
data.checked_count integeroptional
data.total_count integeroptional
data.meals The meals the list is buying for, one per recipe and the night it is planned for, ordered by night then recipe name; a recipe added from its own page comes last with date null. Each is {key, date, recipe_id, recipe_name, cook, item_ids, checked_count, item_count}. key is stable for the (recipe, night) pair, cook is the planned meal's cook ({id, name, user_id, pending}, as on a meal plan entry) or null, and item_ids are the ids of its lines in items. A line two meals share is in both. Present when items are. objectoptional
data.created_at string or null
data.updated_at string or null
data.price_summary object or null
data.price_summary.estimated_total number
data.price_summary.currency string
data.price_summary.priced_count integer
data.price_summary.item_count integer
data.price_summary.remaining_total number
data.price_summary.remaining_count integer
data.price_summary.remaining_item_count string
data.price_summary.checked_total number
data.price_summary.checked_count integer

GET /shopping-list/items/{item}

Get a single shopping list item

Parameters

Parameters for Get a single shopping list item
Field Type
item The item UUID stringrequired

Example request

curl -X GET https://thesaucery.nz/api/v1/shopping-list/items/{item} \
  -H "Authorization: Bearer sc_key_..."

Response 200 · OK

Response body for Get a single shopping list item
Field Type
data object
data.id string
data.name string
data.quantity What to buy: a count is whole and a weight or volume is rounded up to what a shelf sells. A line the family typed in is returned as typed. string
data.unit string
data.display_quantity The amount as it reads before the name, in the unit it is bought in: "2.1 l", "5 tbsp", or "about 5" for a weight of something bought by the piece. Null when the line has no amount. string
data.quantity_note A second line about the amount, or null: the weight behind an "about" count ("540 g altogether"), or "You probably have this in the pantry" when probably_at_home is true. string
data.probably_at_home True for a spoonful of something from the pantry aisles, or a splash of oil or sauce, that most kitchens already have. The line stays on the list. string
data.group string or null
data.is_checked boolean
data.checked_by string
data.checked_by_name The name of the family member who ticked the line, or null when it is not ticked or the tick predates the record. string or null
data.checked_at When the line was ticked, ISO 8601, or null. string or null
data.added_by Who put the line on the list, {id, name}: the person who typed it, asked the assistant for it, or added the plan, recipe or staples it came from. Null for lines added before this was recorded and for lines added through an integration key. object or null
data.added_by.id string
data.added_by.name string
data.recipe_id string
data.staple_id string
data.is_manual boolean
data.is_skipped boolean
data.source_type enum
data.recipe_names objectoptional
data.contributions Per-recipe prep sub-lines, e.g. {recipe_name: "Roast Chicken Dinner", detail: "sliced, raw", date: "2026-10-12"}. Lets clients render the base name as the row with each recipe's specifics underneath. date is the night the recipe is planned for (Y-m-d), the earliest when the week has it twice, and null for a recipe added from its own page. object[]optional
data.contributions[].recipe_id string
data.contributions[].recipe_name string
data.contributions[].detail string
data.contributions[].date string or null
data.is_on_special boolean
data.price string or null

Other responses

Other responses for Get a single shopping list item
Status Meaning
403 Forbidden
404 Not found

DELETE /shopping-list/items/{item}

Remove an item from the shopping list

Parameters

Parameters for Remove an item from the shopping list
Field Type
item The item UUID stringrequired

Example request

curl -X DELETE https://thesaucery.nz/api/v1/shopping-list/items/{item} \
  -H "Authorization: Bearer sc_key_..."

Response 200 · OK

Response body for Remove an item from the shopping list
Field Type
message string

Other responses

Other responses for Remove an item from the shopping list
Status Meaning
403 Forbidden
404 Not found

POST /shopping-list/items

Add an item to the shopping list

Request body

Request body for Add an item to the shopping list
Field Type
name stringrequired
quantity number or null
unit string or null
group string or null

Example request

curl -X POST https://thesaucery.nz/api/v1/shopping-list/items \
  -H "Authorization: Bearer sc_key_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"name"}'

Response 200 · OK

Response body for Add an item to the shopping list
Field Type
data object
data.id string
data.name string
data.quantity What to buy: a count is whole and a weight or volume is rounded up to what a shelf sells. A line the family typed in is returned as typed. string
data.unit string
data.display_quantity The amount as it reads before the name, in the unit it is bought in: "2.1 l", "5 tbsp", or "about 5" for a weight of something bought by the piece. Null when the line has no amount. string
data.quantity_note A second line about the amount, or null: the weight behind an "about" count ("540 g altogether"), or "You probably have this in the pantry" when probably_at_home is true. string
data.probably_at_home True for a spoonful of something from the pantry aisles, or a splash of oil or sauce, that most kitchens already have. The line stays on the list. string
data.group string or null
data.is_checked boolean
data.checked_by string
data.checked_by_name The name of the family member who ticked the line, or null when it is not ticked or the tick predates the record. string or null
data.checked_at When the line was ticked, ISO 8601, or null. string or null
data.added_by Who put the line on the list, {id, name}: the person who typed it, asked the assistant for it, or added the plan, recipe or staples it came from. Null for lines added before this was recorded and for lines added through an integration key. object or null
data.added_by.id string
data.added_by.name string
data.recipe_id string
data.staple_id string
data.is_manual boolean
data.is_skipped boolean
data.source_type enum
data.recipe_names objectoptional
data.contributions Per-recipe prep sub-lines, e.g. {recipe_name: "Roast Chicken Dinner", detail: "sliced, raw", date: "2026-10-12"}. Lets clients render the base name as the row with each recipe's specifics underneath. date is the night the recipe is planned for (Y-m-d), the earliest when the week has it twice, and null for a recipe added from its own page. object[]optional
data.contributions[].recipe_id string
data.contributions[].recipe_name string
data.contributions[].detail string
data.contributions[].date string or null
data.is_on_special boolean
data.price string or null

Other responses

Other responses for Add an item to the shopping list
Status Meaning
201 Created
422 Validation error

POST /shopping-list/items/{item}/check

Toggle an item's checked status

Parameters

Parameters for Toggle an item's checked status
Field Type
item The item UUID stringrequired

Example request

curl -X POST https://thesaucery.nz/api/v1/shopping-list/items/{item}/check \
  -H "Authorization: Bearer sc_key_..."

Response 200 · OK

Response body for Toggle an item's checked status
Field Type
data object
data.id string
data.name string
data.quantity What to buy: a count is whole and a weight or volume is rounded up to what a shelf sells. A line the family typed in is returned as typed. string
data.unit string
data.display_quantity The amount as it reads before the name, in the unit it is bought in: "2.1 l", "5 tbsp", or "about 5" for a weight of something bought by the piece. Null when the line has no amount. string
data.quantity_note A second line about the amount, or null: the weight behind an "about" count ("540 g altogether"), or "You probably have this in the pantry" when probably_at_home is true. string
data.probably_at_home True for a spoonful of something from the pantry aisles, or a splash of oil or sauce, that most kitchens already have. The line stays on the list. string
data.group string or null
data.is_checked boolean
data.checked_by string
data.checked_by_name The name of the family member who ticked the line, or null when it is not ticked or the tick predates the record. string or null
data.checked_at When the line was ticked, ISO 8601, or null. string or null
data.added_by Who put the line on the list, {id, name}: the person who typed it, asked the assistant for it, or added the plan, recipe or staples it came from. Null for lines added before this was recorded and for lines added through an integration key. object or null
data.added_by.id string
data.added_by.name string
data.recipe_id string
data.staple_id string
data.is_manual boolean
data.is_skipped boolean
data.source_type enum
data.recipe_names objectoptional
data.contributions Per-recipe prep sub-lines, e.g. {recipe_name: "Roast Chicken Dinner", detail: "sliced, raw", date: "2026-10-12"}. Lets clients render the base name as the row with each recipe's specifics underneath. date is the night the recipe is planned for (Y-m-d), the earliest when the week has it twice, and null for a recipe added from its own page. object[]optional
data.contributions[].recipe_id string
data.contributions[].recipe_name string
data.contributions[].detail string
data.contributions[].date string or null
data.is_on_special boolean
data.price string or null

Other responses

Other responses for Toggle an item's checked status
Status Meaning
403 Forbidden
404 Not found