{"openapi":"3.1.0","info":{"title":"The Pantry Butler","version":"1.0.0","description":"A kitchen that knows what it holds. Read what is in someone's pantry, add to it, and ask the one question a recipe search cannot answer: what can they actually cook tonight, with what they already have.\n\nTokens are created by the account holder at https://thepantrybutler.com/account and carry either `read` or `write` scope. A token reaches only these endpoints — never billing, account settings or deletion.","contact":{"name":"The Pantry Butler","url":"https://thepantrybutler.com/api-docs"},"x-mcp-server":"https://thepantrybutler.com/mcp"},"servers":[{"url":"https://thepantrybutler.com"}],"paths":{"/api/v1/me":{"get":{"summary":"Who this token acts for","description":"Plan, recipe count and remaining allowance. Never returns an email address.","operationId":"getme","security":[{"bearerAuth":[]}],"x-required-scope":"read","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}}}},"/api/v1/cook-tonight":{"get":{"summary":"What can I cook right now?","description":"Every saved recipe scored against what is actually in this person's kitchen, best matches first. `can_make: true` means nothing is missing. This is the question the app exists to answer and the one no recipe search can.","operationId":"getcooktonight","security":[{"bearerAuth":[]}],"x-required-scope":"read","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}}}},"/api/v1/pantry":{"get":{"summary":"What is in the kitchen","description":"Items with their location (pantry, fridge, freezer) and expiry where known.","operationId":"getpantry","security":[{"bearerAuth":[]}],"x-required-scope":"read","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}}},"post":{"summary":"Add items to the kitchen","description":"Send plain language — \"2 lemons\", \"half a bag of spinach\". Up to 50 at a time. Optional `location` of pantry, fridge or freezer.","operationId":"postpantry","security":[{"bearerAuth":[]}],"x-required-scope":"write","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/v1/recipes":{"get":{"summary":"Saved recipes","description":"Title, source credit, source link and timings for up to 200 recipes.","operationId":"getrecipes","security":[{"bearerAuth":[]}],"x-required-scope":"read","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}}},"post":{"summary":"Save a recipe — a draft, or a link to queue","description":"Takes either shape. Send a DRAFT ({ title, ingredients, steps, … }) and it is saved to the cookbook — this is how you finish an import. Send a bare { url } and it returns 202 with { accepted, url, imported: false }: the link is checked against the account's recipe limit and nothing is imported. The full flow is POST /api/v1/capture to read the recipe, then POST the draft back here.","operationId":"postrecipes","security":[{"bearerAuth":[]}],"x-required-scope":"write","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/v1/capture":{"post":{"summary":"Read a recipe out of a link","description":"POST { url }. Runs the AI extraction and returns { draft, confidence }. The draft is NOT saved — POST it to /api/v1/recipes to keep it. Counts against the account's AI fair-use allowance, and a link somebody already captured is served from cache and costs nothing. A low confidence means the page gave too little to work from: the draft comes back mostly empty with `__note` explaining why and `__paste` suggesting where the recipe probably is — social posts often keep the ingredients in the first comment, and some sites answer a server with a sign-in wall rather than the post.","operationId":"postcapture","security":[{"bearerAuth":[]}],"x-required-scope":"write","responses":{"200":{"description":"OK"},"202":{"description":"Accepted, not yet imported — see the endpoint description"},"401":{"description":"Missing or invalid bearer token"},"403":{"description":"The token lacks the required scope"},"429":{"description":"Rate limited — 120 requests a minute per token"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A token beginning `pb_live_`. Scopes: read, write. `write` implies `read`."}}},"security":[{"bearerAuth":[]}]}