Skaffi
Shopping list, pantry, recipes
- Category
- Pending
- Primary Subcategory
- Pending
Integration details
Description
Skaffi keeps a household's shopping list, pantry and recipes — and it is built so that your assistant does the typing. Connect it once, then a sentence in the chat is enough: "Put oat milk on the list." "What can I cook tonight with what we have?" What your assistant can do: - Shopping list: see what is on it; add one or many items at once with amount, unit and a note; remove items; check the whole list out after shopping in one step — with the pantry on, everything lands in stock. - Pantry (can be switched off per household): see what is at home with amounts and expiry dates (estimates are marked as such); ask what expires soon; consume stock after cooking, oldest batch first; correct a wrong amount, unit or name. - Recipes: save a recipe with structured ingredients — your own, from a link, or one the assistant just suggested; put its ingredients on the shopping list in one step; copy it into another of your households. - People at the table: read the household's diets, allergies and taste preferences, plus the food profiles friends have released to you, so meal suggestions fit everyone; in a shared household, add or remove guests for an upcoming meal. - Households: list them and switch which one is active. Everything happens in the active household. The web app at skaffi.de shows the same data — plan in the chat, shop from your phone. Reading and changing are cleanly separated: every tool is annotated, so your assistant knows what only looks and what writes. Skaffi is a German service, hosted in Germany. Food-profile entries such as allergies are stored only after your separate, explicit consent, are used for meal planning only, and are deleted when you withdraw it. Skaffi never sends your data to an AI provider by itself — only your own assistant, on your own instruction, reads it. What you need: a Skaffi account — connect, sign in, done. Setup help and troubleshooting: skaffi.de/docs
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Category
- Pending
- Primary Subcategory
- Pending
- Secondary Subcategories
- None listed
- Brand
- Unknown
- Access
- Account required
- First tracked
- 2026-09-17
- Tool count
- 29
- Geography
- US
The broad Category that contains the Primary Subcategory.
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competitive lineup
29 tools agents can invoke
Add products to the pantry (a big grocery run) — all in one call. Each item: name (required), and optional quantity (number), unit, category, expiry_date (YYYY-MM-DD; omit to use the default), expiry_source ("package" or "estimate", default "estimate" — see below). unit and category are enums in this tool's schema — a client that validates rejects a wrong value before sending, an unvalidated one gets a schema error naming the allowed values. For 'I bought all this' where some/all were on the shopping list, prefer record_purchases instead so they also get removed from the list. Set expiry dates HERE when you can: from a purchase date or freshness hint (e.g. a fridge photo, "bought these ~2 weeks ago"), compute each item's expiry and pass it now — that avoids a follow-up correction (correct_pantry_items). expiry_source: "package" ONLY for a date read off the packaging (or a photo of it); "estimate" — the default — for anything you computed. A wrong "package" makes a guess look verified. Lines are processed independently: an invalid line (e.g. an unusable quantity or date) is skipped and reported per line; the valid lines are still stored. Example: [{"name": "Reis", "quantity": 1, "unit": "kg"}, {"name": "Quark", "quantity": 500, "unit": "g", "expiry_date": "2026-07-15", "expiry_source": "package"}]
add_pantry_items
Add products to the shopping list (things still to BUY) — all of them in one call, e.g. the missing ingredients of a recipe at once. Items already on the list with the SAME unit and a note that says the same thing are merged (quantity increased), not duplicated — so you do NOT need to read the list first. A DIFFERING note deliberately stays its own line ('Brot' and 'Brot — glutenfrei' are two lines), so a variant someone needs never disappears into a plain entry. Each item is an object with keys: - name (required): short, generic product name, e.g. "Bio Milch". - quantity_value: optional numeric amount, e.g. 2. - quantity_unit / category: optional; both are enums in this tool's schema, so a client that validates rejects a wrong value before sending and an unvalidated one gets a schema error naming the allowed values. Map other unit words to the tokens (pieces/Stk/pcs → Stück, litre/liter → L, grams → g); omit the category if unsure rather than inventing one. - notes: optional note, e.g. "nur laktosefrei". - icon: optional single emoji for a product the catalog does not know yet (e.g. "🧈" for Kräuterbutter) — used ONLY when the product is created. Pass it (with category) for anything that is not an everyday staple, else the new entry shows a neutral 📦. Anything but an emoji is rejected. Lines are processed independently: an invalid line (e.g. an unusable amount or icon) is skipped and reported per line; the valid lines are still added. Example: [{"name": "Spaghetti", "quantity_value": 500, "quantity_unit": "g"}, {"name": "Eier", "quantity_value": 4, "quantity_unit": "Stück"}, {"name": "Kräuterbutter", "category": "Milchprodukte & Käse", "icon": "🧈"}]
add_shopping_items
Add people you're cooking for who aren't reflected in get_household_preferences (no Skaffi account, or a member who didn't share) to the active SHARED household — the whole guest list in one call ("Anna vegetarisch, Ben ohne Gluten" is one call with two guests). They then feed get_household_preferences. Only works in a shared household, not your private list (create a shared household for the event first). A guest is a third party, so for data-protection reasons only NEUTRAL taste data is stored — NO allergies and NO religious diet. If those matter, invite the guest so they can enter them themselves in the app; do NOT try to record them here (or squeeze them into another field). Each guest: name (required — the guest's name or label, e.g. "Oma" or "Lisa"), and optional: - diet: one of (use this EXACT wording, matching members): Omnivor, Vegetarisch, Vegan, Pescetarisch, Flexitarisch. Omit if unknown. - cuisines: liked-cuisine keys — any of: italienisch, asiatisch, mediterran, deutsch, mexikanisch, indisch, orientalisch, amerikanisch. Unknown keys are ignored. - diet_goals: eating-style keys — any of: low_carb, high_protein, keto, low_sugar, low_fat, whole_food, intermittent_fasting. - spice_level: 1 (mild), 2 (medium) or 3 (spicy). Omit if unknown. - portion_size: "klein", "normal" or "gross". Omit if unknown. Example: [{"name": "Omar", "diet": "Omnivor", "cuisines": ["orientalisch"], "spice_level": 3}, {"name": "Anna", "diet": "Vegetarisch"}]
add_meal_guests
Put ALL ingredients of saved recipes onto the ACTIVE household's shopping list — several recipes in one call ("setz Lasagne und Tiramisu auf die Liste"). Typical flow: save_recipe (with structured `ingredients`) → add_recipes_to_shopping_list. Each name is a recipe's TITLE exactly as shown by list_recipes (matched case-insensitively), e.g. "Spaghetti Carbonara". The recipes must be in the active household (switch_household first if they live elsewhere). Names are processed independently and answered per line. If a recipe has no structured ingredient list, its line says so — then take the ingredients from the recipe text you already have and call add_shopping_items instead; repeating this tool will not help.
add_recipes_to_shopping_list
Put the ingredients of every RECIPE planned in a week onto the ACTIVE household's shopping list, in one call. Uses the same logic as add_recipes_to_shopping_list, for the whole week at once. Free-text meals have no ingredients and are skipped; a recipe planned on two days counts twice. Pass `start_date` (any YYYY-MM-DD in the week; snaps to Monday) for another week, or omit it for the current week. If nothing is added, the week has no recipe meals with a structured ingredient list — plan recipes (plan_meals with `recipe`) or make sure they carry ingredients; calling again will not help.
add_week_to_shopping_list
Finish the shopping in one call: clears the WHOLE shopping list at once because the user bought everything on it. Use for "I bought everything", "done/finished shopping", "check out the list". If the pantry is ON, every item also goes into the pantry with a default expiry from its shelf life. If the pantry is OFF, the list is simply cleared and nothing is stored. This empties the ENTIRE list — if the user only bought SOME items, use record_purchases with just those instead.
checkout_shopping_list
Consume amounts of products from the pantry (e.g. after cooking) — all ingredients of a meal in ONE call. Draws from the soonest-expiring batches of each product first. Each item: name (matched case-insensitively against the pantry), amount (required), unit (optional). Metric units convert automatically: consuming 500 g from a batch stored as "1 kg" works and leaves 0.5 kg (g↔kg and ml↔L only). Nothing else ever converts — a line whose unit cannot be served (e.g. grams asked, Stück stored) is refused and names the stored unit; restate that ONE line in the stored unit. Omit the unit to consume across any unit, or check get_pantry first if unsure. Lines are processed independently and answered per line. If a line says some amount could not be consumed, the pantry simply doesn't hold that much — report it to the user; do NOT retry the same line. Example: [{"name": "Mehl", "amount": 500, "unit": "g"}, {"name": "Eier", "amount": 2, "unit": "Stück"}]
consume_pantry_items
Copy a recipe from the ACTIVE household into ANOTHER of the user's households, as an independent copy (edits to one don't affect the other) — e.g. keep an event recipe in your private cookbook, or push it into a shared household. NOTE: the user's "private cookbook" = their PRIVATE household — call get_households for its exact name. The recipe must exist in the currently active household (the copy source); if unsure, call list_recipes / get_households first.
copy_recipe
Correct a product's catalog entry — affects ALL batches and future entries. Use this when the product itself is wrong, not just a single pantry batch: - Brand or pack name slipped in: rename_to a generic name ("Sagrotan Hygiene-Spüler" → "Hygiene-Spüler", "Dr. Oetker Quarktaschen Zitrone" → "Quarktaschen"). - Wrong category (changes grouping and the default emoji). - Emoji doesn't fit. PREFER omitting `icon` — without an icon the app shows the category emoji, which is usually correct. Only set an emoji if a more specific match is obvious (🥛 milk, 🧺 laundry, 🧴 cleaner). - Default shelf life is off (used to auto-fill expiry on future purchases; 0 means non-perishable). For changes to single pantry batches (wrong unit/amount/expiry on one addition), use correct_pantry_items instead.
correct_product
Correct pantry batches that were added with wrong values — amount, unit and/or expiry date, several products in one call. Each line acts on the SOONEST-expiring batch of the named product. Use this to fix mistakes instead of removing the item and adding it again (which would lose the activity-log link). Each item names the product and passes ONLY what should change: - quantity: new amount, or omit the key to keep the current one. Wrong unit AND amount (added "Quark" as 1 Stück, should be 500 g): pass quantity=500, unit="g". - unit: new unit, or omit to keep. - expiry_date: a YYYY-MM-DD string to SET the expiry; the literal null to REMOVE it ("no expiry"); or omit the key to KEEP the current date. null and omitting are NOT the same — null clears, omitting keeps. - expiry_source: "package" ONLY for a NEW date read off the packaging (or a photo of it); "estimate" — the default — for anything you calculated. A wrong "package" makes a guess look verified. To rename, re-categorize, change the emoji, or change the default shelf life of the PRODUCT itself (applies to all its batches and future purchases), use correct_product instead. Lines are processed independently and answered per line. Example: [{"name": "Quark", "quantity": 500, "unit": "g"}, {"name": "Milch", "expiry_date": "2026-07-15", "expiry_source": "package"}, {"name": "Reis", "expiry_date": null}]
correct_pantry_items
Permanently delete a recipe from the ACTIVE household (not archiving).
remove_recipe
List your accepted friends and whether each has released their food profile to you — the people you can pull into a group meal plan alongside your household members and any meal guests. Read-only. Sending or accepting friend requests and releasing your OWN profile are done by you in the Skaffi app (skaffi.de/am-tisch/freunde), not here — they are consent actions that belong to a person, not an assistant.
list_friends
List the user's households and show which one is currently ACTIVE. Use this to answer "which household am I in?" / "what households do I have?". All other tools (shopping list, pantry) operate on the ACTIVE household.
get_households
List the meal guests (account-less people) of the active household. These are the guests get_household_preferences folds into the group plan and headcount — use this to check who's included before planning, or to find the exact names to pass to remove_meal_guests.
list_meal_guests
List the saved recipes in the user's ACTIVE household (title, label, favourite, ingredient count). Use the TITLE shown here as the name argument for add_recipes_to_shopping_list, copy_recipe and remove_recipe. "(favourite)" marks THIS user's personal favourites (each member stars their own; managed in the Skaffi web app, not via these tools).
list_recipes
Plan meals on days of the ACTIVE household's weekly plan — a whole week in one call ("plan mir die Woche"). Each item gives `date` (the day to plan for, as YYYY-MM-DD) and EXACTLY ONE of: - `recipe`: the TITLE of a saved recipe (exactly as list_recipes shows it, matched case-insensitively) — links the plan slot to that recipe, so add_week_to_shopping_list can later shop for it. The recipe must be in the active household (switch_household first if it lives elsewhere). - `text`: a short free-text meal (max 200 chars) for days without a saved recipe (e.g. "Reste", "Essen gehen"). You can plan several meals on the same day (one item per meal). Lines are processed independently and answered per line. Example: [{"date": "2026-08-10", "recipe": "Spaghetti Carbonara"}, {"date": "2026-08-11", "text": "Reste"}]
plan_meals
Read the food profiles your friends have released to you — for planning a meal WITH friends (not just your household). Honours every allergy strictly, respects religious diets, avoids dislikes where possible, fits each diet. Also the right tool when asked what ONE person likes and they are not a household member: friends are not in get_household_preferences, so check here before concluding you don't know the person (#372). Read-only: you can't add friends or change who released to you here — that's done by the user in the Skaffi app (skaffi.de/am-tisch/freunde). Group-planning flow: get_household_preferences (your household) → list_meal_guests / add_meal_guests (people WITHOUT a Skaffi account) → get_friend_preferences (friends WITH a Skaffi account who shared with you) → build the menu → add_shopping_items. Do NOT also create a meal guest for a person whose friend profile you already have here — that would double them.
get_friend_preferences
Record that products were BOUGHT: each is ADDED to the pantry stock and removed from the shopping list if it was on it. Use for "I bought X (and Y and Z)" — the whole bag in one call. Prefer this for bought items because it also clears the list. When a product is on the list in several variant lines (same product, different notes), its name picks the line to clear: 'glutenfreies Brot' clears the gluten-free line, plain 'Brot' the plain one. With ONLY differing variants on the list a plain name clears none of them — repeat that one item naming the variant that was bought. For "I bought EVERYTHING / done shopping", call checkout_shopping_list once instead. If this household has the pantry turned OFF, this tool is unavailable; use remove_shopping_items to clear the items off the list. Each item: name (required, e.g. "Bananen"), and optional quantity (amount bought, default 1, added to any existing stock), unit (default Stück), category, expiry_date, expiry_source. unit and category are enums in this tool's schema. Set each expiry intelligently: a stated date ("Quark mit MHD 15.07.") → pass it as YYYY-MM-DD; a freshness hint ("Banane, schon gut braun") → estimate a realistic date (e.g. today + 2 days) and pass it; nothing said → omit expiry_date, and a default is ESTIMATED from the product's typical shelf life (a guess, not a date off the package — say so if the user asks whether something is still good). expiry_source: "package" ONLY for a date read off the packaging (or a photo of it); "estimate" — the default — for anything you calculated, freshness hints included. A wrong "package" makes a guess look verified. Lines are processed independently and answered per line. Example: [{"name": "Milch", "quantity": 2, "unit": "L"}, {"name": "Quark", "quantity": 500, "unit": "g", "expiry_date": "2026-07-15", "expiry_source": "package"}]
record_purchases
Remove items from the shopping list by name WITHOUT buying them (changed their mind, duplicates, etc.) — all in one call. For "I bought X", use record_purchases instead — it also stores the items in the pantry. Removes whole entries regardless of quantity (no partial remove). Case-insensitive and resolves catalog synonyms the same way adding does ('Klopapier' removes 'Toilettenpapier'). Since a product can be on the list in several VARIANT lines (same product, different notes — e.g. 'Brot' and 'Brot — glutenfrei'), each name decides which line: 'glutenfreies Brot' removes the gluten-free line, plain 'Brot' the plain line. If only variant lines exist and a name names none of them, that line is not removed and the variants are listed — repeat that ONE name with the variant included. Names are processed independently and answered per line. "'X' is not on the shopping list." is a normal result, not an error; do not retry it.
remove_shopping_items
Remove meal guests (account-less people) from the active household by name (case-insensitive) — e.g. the whole guest list when the event is cancelled. If unsure of the exact names, call list_meal_guests first. Names are processed independently and answered per line; "no meal guest called X" is a normal result, not an error — do not retry it.
remove_meal_guests
Remove planned meals from days of the ACTIVE household's plan ("streich Montag und Dienstag"). Each item: date (the day the meal is planned for, as YYYY-MM-DD) and name (the meal to remove — the recipe title or the free text exactly as get_meal_plan shows it, matched case-insensitively). If several meals on that day share the name, the first is removed. Lines are processed independently and answered per line. "No meal called X planned" is a normal result, not an error; call get_meal_plan to see what's actually planned.
remove_planned_meals
Save a recipe into the user's ACTIVE household so it isn't lost — whether the user supplied it, pasted a link, or approved one you proposed. In their private household it's their personal cookbook; in a shared household everyone there sees it. The `title` you set is how you'll refer to this recipe later (add_recipes_to_shopping_list, copy_recipe, remove_recipe all match on it). Write the full recipe into `body` (ingredients + steps; markdown is fine). If it came from a website, pass its URL as source_url. `label` optionally groups it (e.g. an event name). ALMOST ALWAYS pass `ingredients` as a structured list, so the user can later put them on the shopping list in one step (add_recipes_to_shopping_list, or the web button). Each entry: `name` (required, short & GENERIC — "Spaghetti", not "Barilla Spaghetti", so it merges cleanly) plus optional `quantity` (number) and `unit` (EXACTLY one of: Stück, g, kg, ml, L, Pkg). Use the closest unit; if none fits, omit it. Example: [{"name": "Spaghetti", "quantity": 400, "unit": "g"}, {"name": "Knoblauch", "quantity": 3, "unit": "Stück"}, {"name": "Salz"}]. After saving, if the user also wants to shop for it, call add_recipes_to_shopping_list(names=[<this title>]) — do NOT also call add_shopping_items for the same recipe (that would duplicate).
save_recipe
Set which SHOP the named products belong to — "Milch, Butter, Käse → Edeka" in one call. Pass an empty/null store to clear instead. apply_to picks the reach: - "trip" (the default): a per-line override for THIS trip only — e.g. re-cluster by today's offers. It needs the item to be ON the list, wins over the product's default for that one entry, and clearing it falls back to the default shop, then "no store". - "product": remember the shop as the product's DEFAULT ("Milch → Edeka") so it clusters there on every future list without setting it each time. Existing per-trip overrides keep winning for their entry. The shop is free text and remembered per household; common chains to reuse the exact spelling of: Edeka, Rewe, Lidl, Aldi, Penny, Netto, Kaufland, dm, Rossmann. Names are processed independently and answered per line. "Not on the list" / "no product called X" is a normal result, not an error — do not retry it.
set_shopping_store
Call this FIRST in a Skaffi conversation, before any other Skaffi tool. It reports this account's state — active household, who eats there, whether the pantry is on, whether friends' profiles are readable — and the tool chains that actually work for it. Accounts differ in ways that decide which tools do anything: a household with the pantry turned off refuses every pantry tool, and friends' profiles are not readable on every account. Reading that state costs one call and saves running into refusals. Safe to call again at any point — it only reads. Worth repeating after switch_household, since every line below describes the ACTIVE household.
get_started
Set the ACTIVE household by name. The active household is a single persistent selection that EVERY other tool (shopping list, pantry, preferences, guests, recipes) acts on — so switch FIRST, then act. This also moves the active household in the Skaffi web app. Matches the EXACT household name (case-insensitive): a decorated phrase like "my WG household" will NOT match a household stored as "WG". If unsure of the exact name, call get_households first. The user's personal list is a household too (usually "Deine Liste"). On "no household called X", re-read the real names rather than inventing one.
switch_household
Aggregate the food profiles of everyone eating in the ACTIVE household — every member plus any meal guests you added — with a total headcount. Call this FIRST whenever you plan food for the group (a recipe, a weekly plan, a dinner/event), then build a menu that honours every allergy strictly, respects religious diets, avoids dislikes where possible, fits each person's diet (e.g. no meat for a vegetarian), leans on shared likes/cuisines, and is PORTIONED for the returned headcount. Typical event flow: switch_household to the event's household → add_meal_guests for the attendees without a Skaffi account → get_household_preferences → propose the menu → add_shopping_items for the ingredients. SHARING (important to read the output correctly): each member chooses what to share per household, in two tiers — taste (diet/cuisines/likes/…) and sensitive (allergies + religious diet). A line may therefore say a member did NOT share, or shared taste but withheld allergies. "Not shared" ≠ "no preferences" — it means their toggle is off; they still COUNT in the headcount, so plan/portion for them and, if their diet matters, ask the user or have them enable sharing in the app. If the user names eaters not listed here, capture them with add_meal_guests and call this tool again. Diet values use this exact wording: Omnivor, Vegetarisch, Vegan, Pescetarisch, Flexitarisch. Empty fields mean unspecified — don't assume.
get_household_preferences
Return what's currently AT HOME (the pantry / stock on hand), with amounts and expiry dates. This is the source of truth for "what do I have", "can I cook X", "what did I already buy" and "what's running low / expiring". NOT the shopping list. Pass expiring_within_days to filter to what expires within that many days (including already expired), soonest first — "was läuft ab / what should I use up this week?" is expiring_within_days=7. A NEGATIVE number is meaningful and allowed: -5 lists what has been expired for at least 5 days ("was muss weg / what should I throw out?"), because the cutoff is today + days. Omit the parameter for the whole pantry. Expiry dates carry their ORIGIN: a date derived from the product's typical shelf life is marked "(estimated)"; an unmarked date was read off the package. So this is per entry, not a blanket caveat — an estimate is a planning hint, never a food-safety verdict, and for anything the user is deciding whether to still eat, the date printed on the package is what counts.
get_pantry
Return what still needs to be BOUGHT (the shopping list). This is NOT what's at home — for stock on hand use get_pantry. Use this to answer "what's on my shopping list / what do I still need to buy?". Pass group_by="store" to get the same list grouped by SHOP, so the user can split a trip ("you get the Edeka things, I'll do Netto") — the answer to "what do I buy where / group my list by store". Each item's shop is its per-line override, else the product's default shop, else the "Ohne Laden" (no store) group. To CHANGE where an item belongs, use set_shopping_store.
get_shopping_list
Read the ACTIVE household's meal plan for one week (Monday–Sunday). Use this to answer "what's the plan this week?" or before proposing changes. Each day lists its planned meals — either a saved recipe (by title) or a free-text meal. Pass `start_date` (any YYYY-MM-DD in the wanted week; it is snapped to that week's Monday) to look at another week; omit it for the current week.
get_meal_plan
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.