API reference
Food
Recipes, collections, the meal plan, shopping lists, the pantry, suggestions and importing.
See Food. Food is personal: these routes take no organization. The plan, shopping lists and pantry are the household's, so everyone in it reads and changes the same ones. A key needs food:read (and food:write to change things) and acts for the person who made it. Public recipe links and the plan's calendar feed answer without a sign-in, at /v1/hooks/food.
GET /v1/hooks/food/r/:token
A recipe by its public link (no sign-in): the recipe without its owner's notes or rating. 404 once the link is off.
Auth: none
| Path parameter | Description |
|---|---|
:token | A secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed. |
Response 200
{
recipe: {
version: number
createdAt: number
updatedAt: number
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags: string[]
ownerId: string
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
recipeId: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
notes?: string
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
images: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}[]
nutrition: {
perServing: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
total: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
coverage: number
unmatched: string[]
}
allergens: string[]
diets: string[]
access: "owner" | "household" | "public"
ownerName?: string
publicUrl?: string
cost?: {
total: number
perServing: number
coverage: number
priced: number
measured: number
}
}
}GET /v1/hooks/food/plan/:token
A household's meal plan as an iCalendar feed (<token>.ics, no sign-in): two weeks back to eight ahead, at meal times.
Auth: none
| Path parameter | Description |
|---|---|
:token | A secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed. |
Response 200 with no body.
GET /v1/me/food/settings
Your Food settings (measures, spoons, servings, diets, allergens, suggestions, Health logging, week start) and your household.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
settings: {
measure: "metric" | "original" | "imperial"
spoons: "au" | "us"
defaultServings: number
diets: string[]
avoidAllergens: string[]
weeknightMinutes?: number
weatherSuggestions: boolean
healthLogging: boolean
calendar?: {
orgId: string
calendarId: string
}
weekStartsOn: number
receiptMail?: {
orgId: string
mailboxId: string
address: string
since: number
backfilledAt?: number
}
useSoonAlerts: boolean
voiceControl: boolean
weeklyBudget?: number
timeZone?: string
}
home: {
homeId: string
name: string
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
createdAt: number
receipts?: {
token?: string
autoAdd?: boolean
confirmation?: {
code?: string
link?: string
at: number
}
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
PATCH /v1/me/food/settings
Changes the person's Food settings (only the fields given; null clears one).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
measure | "original" | "metric" | "imperial" | No | |
spoons | "au" | "us" | No | |
defaultServings | integer | No | 1–50 |
diets | [] | No | up to 10 items |
avoidAllergens | [] | No | up to 20 items |
weeknightMinutes | integer | No | 10–240; can be null |
weatherSuggestions | boolean | No | |
healthLogging | boolean | No | |
weekStartsOn | integer | No | 0–6 |
useSoonAlerts | boolean | No | |
voiceControl | boolean | No | |
weeklyBudget | number | No | 1–100000; can be null |
timeZone | string | No | up to 60 characters; matches ^[A-Za-z_]+(\/[A-Za-z0-9_+-]+){0,2}$ |
Also checked: Unknown fields are rejected.
Response 200
{
settings: {
measure: "metric" | "original" | "imperial"
spoons: "au" | "us"
defaultServings: number
diets: string[]
avoidAllergens: string[]
weeknightMinutes?: number
weatherSuggestions: boolean
healthLogging: boolean
calendar?: {
orgId: string
calendarId: string
}
weekStartsOn: number
receiptMail?: {
orgId: string
mailboxId: string
address: string
since: number
backfilledAt?: number
}
useSoonAlerts: boolean
voiceControl: boolean
weeklyBudget?: number
timeZone?: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/home
Your household: its name and members (made, yours alone, the first time).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
home: {
homeId: string
name: string
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
createdAt: number
receipts?: {
token?: string
autoAdd?: boolean
confirmation?: {
code?: string
link?: string
at: number
}
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
PATCH /v1/me/food/home
Renames your household.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | 1–60 characters |
Response 200
{
home: {
name: string
homeId: string
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
createdAt: number
receipts?: {
token?: string
autoAdd?: boolean
confirmation?: {
code?: string
link?: string
at: number
}
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/home/invites
An invitation link into your household, good for a week.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 201
{
invite: {
code: string
url: string
expiresAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/home/invites/:code
What an invitation is for: the household's name, who invited you and how many are in it.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:code | Mirage invite code (8 letters and digits); under me/food a household invitation's code (from its link). |
Response 200
{
invite: {
homeName: string
invitedBy: string
members: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/home/invites/:code/accept
Joins the household (your own waits for you; refused while yours has others in it).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:code | Mirage invite code (8 letters and digits); under me/food a household invitation's code (from its link). |
Response 200
{
home: {
homeId: string
name: string
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
createdAt: number
receipts?: {
token?: string
autoAdd?: boolean
confirmation?: {
code?: string
link?: string
at: number
}
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/home/leave
Leaves the household, back to your own (its owner can't while others are in it).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 200
{
home: {
homeId: string
name: string
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
createdAt: number
receipts?: {
token?: string
autoAdd?: boolean
confirmation?: {
code?: string
link?: string
at: number
}
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
DELETE /v1/me/food/home/members/:userId
Removes someone from your household (its owner only).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
Response 200
{
home: {
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
homeId: string
name: string
createdAt: number
receipts?: {
token?: string
autoAdd?: boolean
confirmation?: {
code?: string
link?: string
at: number
}
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/recipes
The person's recipes and their household's shared ones as cards, searched and filtered (limit: at most 1000, 200 by default).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Notes |
|---|---|---|---|
q | string | No | up to 200 characters |
cuisine | string | No | up to 40 characters |
course | string | No | up to 40 characters |
diet | string | No | up to 40 characters |
tag | string | No | up to 40 characters |
without | string | No | up to 200 characters |
maxMinutes | integer | No | 1–10000; coerced from a string |
ingredient | string | No | up to 60 characters |
favourites | "1" | "true" | No | |
collectionId | string | No | up to 40 characters |
sort | "recent" | "title" | "rating" | "cooked" | "quick" | No | |
household | "1" | "true" | "0" | "false" | No | |
limit | integer | No | 1–1000; coerced from a string |
Response 200
{
recipes: {
createdAt: number
updatedAt: number
title: string
tags: string[]
ownerId: string
shared?: boolean
favourite?: boolean
recipeId: string
servings: number
totalMinutes?: number
cuisines: string[]
courses: string[]
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
diets: string[]
allergens: string[]
kcalPerServing?: number
ingredientIds: string[]
amounts?: {
[key: string]: {
g?: number
ml?: number
each?: number
minor?: true
}
}
measuredLines?: number
image?: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}
}[]
total: number
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/recipes
Adds a recipe. Ingredients and steps may be text (one ingredient per line; steps by paragraph), which Food reads; imageUrls are copied in by the worker.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
title | any JSON | Yes | |
description | any JSON | No | |
servings | integer | No | 1–1000 |
yieldLabel | any JSON | No | |
prepMinutes | integer | No | 0–43200 |
cookMinutes | integer | No | 0–43200 |
totalMinutes | integer | No | 0–43200 |
ingredients | object[] | any JSON | No | up to 150 items |
steps | object[] | any JSON | No | up to 100 items |
cuisines | [] | No | up to 5 items |
courses | [] | No | up to 6 items |
claimedDiets | [] | No | up to 10 items |
tags | string[] | No | up to 30 items; each 1–40 characters, matches ^[\p{L}\p{N}][\p{L}\p{N} '&-]*$, trimmed, lowercased |
difficulty | "easy" | "medium" | "hard" | No | can be null |
spoons | "au" | "us" | No | |
source | object | No | |
source.kind | "manual" | "web" | "photo" | "text" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy" | Yes | |
source.url | string | No | up to 2,000 characters; URL |
source.name | any JSON | No | |
source.author | any JSON | No | |
notes | string | No | up to 20,000 characters |
rating | integer | No | 1–5; can be null |
favourite | boolean | No | |
household | boolean | No | |
images | object[] | No | up to 10 items |
images[].imageId | string | Yes | up to 60 characters |
images[].versionId | string | No | up to 60 characters |
images[].mime | string | No | matches ^image\/(jpeg|png|webp|gif)$ |
images[].width | integer | No | |
images[].height | integer | No | |
images[].alt | any JSON | No | |
imageUrls | string[] | No | up to 10 items; each up to 2,000 characters, URL |
Also checked: Unknown fields are rejected.
Response 201
{
recipe: {
version: number
createdAt: number
updatedAt: number
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags: string[]
ownerId: string
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
recipeId: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
notes?: string
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
images: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}[]
nutrition: {
perServing: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
total: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
coverage: number
unmatched: string[]
}
allergens: string[]
diets: string[]
access: "owner" | "household" | "public"
ownerName?: string
publicUrl?: string
cost?: {
total: number
perServing: number
coverage: number
priced: number
measured: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/recipes/:recipeId
A recipe in full, with signed picture links, nutrition per serving, allergens, diets and your access (owner, household).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Response 200
{
recipe: {
version: number
createdAt: number
updatedAt: number
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags: string[]
ownerId: string
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
recipeId: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
notes?: string
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
images: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}[]
nutrition: {
perServing: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
total: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
coverage: number
unmatched: string[]
}
allergens: string[]
diets: string[]
access: "owner" | "household" | "public"
ownerName?: string
publicUrl?: string
cost?: {
total: number
perServing: number
coverage: number
priced: number
measured: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
PATCH /v1/me/food/recipes/:recipeId
Changes a recipe of the person's (version: refused with 409 when it changed meanwhile).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
title | any JSON | No | |
description | any JSON | No | |
servings | integer | No | 1–1000 |
yieldLabel | any JSON | No | |
prepMinutes | integer | No | 0–43200 |
cookMinutes | integer | No | 0–43200 |
totalMinutes | integer | No | 0–43200 |
ingredients | object[] | any JSON | No | up to 150 items |
steps | object[] | any JSON | No | up to 100 items |
cuisines | [] | No | up to 5 items |
courses | [] | No | up to 6 items |
claimedDiets | [] | No | up to 10 items |
tags | string[] | No | up to 30 items; each 1–40 characters, matches ^[\p{L}\p{N}][\p{L}\p{N} '&-]*$, trimmed, lowercased |
difficulty | "easy" | "medium" | "hard" | No | can be null |
spoons | "au" | "us" | No | |
source | object | No | |
source.kind | "manual" | "web" | "photo" | "text" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy" | Yes | |
source.url | string | No | up to 2,000 characters; URL |
source.name | any JSON | No | |
source.author | any JSON | No | |
notes | string | No | up to 20,000 characters |
rating | integer | No | 1–5; can be null |
favourite | boolean | No | |
household | boolean | No | |
images | object[] | No | up to 10 items |
images[].imageId | string | Yes | up to 60 characters |
images[].versionId | string | No | up to 60 characters |
images[].mime | string | No | matches ^image\/(jpeg|png|webp|gif)$ |
images[].width | integer | No | |
images[].height | integer | No | |
images[].alt | any JSON | No | |
imageUrls | string[] | No | up to 10 items; each up to 2,000 characters, URL |
version | integer | No | ≥ 0 |
Also checked: Unknown fields are rejected. Unknown fields are rejected.
Response 200
{
recipe: {
version: number
createdAt: number
updatedAt: number
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags: string[]
ownerId: string
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
recipeId: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
notes?: string
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
images: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}[]
nutrition: {
perServing: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
total: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
coverage: number
unmatched: string[]
}
allergens: string[]
diets: string[]
access: "owner" | "household" | "public"
ownerName?: string
publicUrl?: string
cost?: {
total: number
perServing: number
coverage: number
priced: number
measured: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
DELETE /v1/me/food/recipes/:recipeId
Deletes a recipe of yours, its pictures (freeing their storage) and its public link.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Response 200
{
recipeId: string
deleted: boolean
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/recipes/:recipeId/copy
Copies a recipe your household shares into your own recipes (pictures copied into your storage).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Response 201
{
recipe: {
version: number
createdAt: number
updatedAt: number
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags: string[]
ownerId: string
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
recipeId: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
notes?: string
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
images: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}[]
nutrition: {
perServing: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
total: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
coverage: number
unmatched: string[]
}
allergens: string[]
diets: string[]
access: "owner" | "household" | "public"
ownerName?: string
publicUrl?: string
cost?: {
total: number
perServing: number
coverage: number
priced: number
measured: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/recipes/:recipeId/cook
Logs that the person cooked it, with a rating and note; logToHealth logs a serving to Health's nutrition (when they turned it on).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
servings | integer | No | 1–100 |
rating | integer | No | 1–5 |
note | any JSON | No | |
at | integer | No | |
logToHealth | boolean | No | |
leftovers | object | No | |
leftovers.portions | integer | Yes | 1–50 |
leftovers.place | "fridge" | "freezer" | Yes | |
leftovers.useBy | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
Also checked: Unknown fields are rejected.
Response 201
{
leftovers?: {
itemId: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
addedBy: string
addedAt: number
source?: "photo" | "manual" | "receipt" | "leftovers" | "barcode"
barcode?: string
leftover?: {
recipeId: string
title: string
portions: number
cookedOn: string
}
}
health?: {
logged: boolean
error?: string
}
cook: {
cookId: string
recipeId: string
title: string
userId: string
at: number
servings?: number
rating?: number
note?: string
loggedToHealth?: boolean
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
PUT /v1/me/food/recipes/:recipeId/link
Turns the recipe's public link on (a new link each time) or off.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
on | boolean | Yes |
Response 200
{
recipeId: string
publicUrl: null | string
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/recipes/:recipeId/notes
Saves the recipe as a Notes page in one of the person's orgs (where they can write Notes).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:recipeId | A recipe's id (rcp_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
orgId | string | Yes | 1–64 characters |
parentId | string | No | up to 64 characters |
Response 201
{
page: {
orgId: string
pageId: string
title: string
}
}Errors
| Status | Message |
|---|---|
400 | Parent page not found. |
400 | The parent page is in the trash. |
400 | Unknown property. |
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
403 | You can't add pages to Notes in that organization. |
404 | Page not found |
409 | This page is in the trash. Restore it to edit. |
413 | Pages can be up to 1 MB. |
POST /v1/me/food/public/:token/copy
Copies a recipe shared by public link into your own recipes.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:token | A secret from a link: an issue automation rule's incoming-webhook token (shown once when issued), a map's share link, a notification recipient's confirm and unsubscribe link, a Drive public link (/l/<token>), or a Mirage interaction's token (itk_…, sent with the interaction, good for 15 minutes), or the token in the link to a file of a message Mirage sent to Discord, or a Marketing link's sealed token (a tracked click, the open pixel, unsubscribe and the preference centre, a double opt-in confirmation); or a Food recipe's public link (/r/<token>) or a household's calendar feed. |
Response 201
{
recipe: {
version: number
createdAt: number
updatedAt: number
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags: string[]
ownerId: string
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
recipeId: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
notes?: string
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
images: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}[]
nutrition: {
perServing: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
total: {
kj: number
kcal: number
protein: number
fat: number
saturatedFat: number
carbs: number
sugars: number
fibre: number
sodium: number
}
coverage: number
unmatched: string[]
}
allergens: string[]
diets: string[]
access: "owner" | "household" | "public"
ownerName?: string
publicUrl?: string
cost?: {
total: number
perServing: number
coverage: number
priced: number
measured: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/history
What you've cooked, newest first, with ratings and notes (limit, 100 by default).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
limit | string | No | 100 |
Response 200
{
cooked: {
cookId: string
recipeId: string
title: string
userId: string
at: number
servings?: number
rating?: number
note?: string
loggedToHealth?: boolean
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/images
Keeps a picture (base64 JPEG, PNG, WebP or GIF, at most 8 MB) in the person's Food drive, metadata removed; use the answer in a recipe's images.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body (up to 10 MB)
| Field | Type | Required | Notes |
|---|---|---|---|
data | string | Yes | 10–12,000,000 characters |
name | string | No | up to 100 characters |
alt | string | No | up to 300 characters |
Response 201
{
image?: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
413 | That's too large. Send pictures of at most 8 MB, four at a time. |
GET /v1/me/food/collections
Your collections, with their recipes in order and their groups.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
collections: {
collectionId: string
ownerId: string
name: string
description?: string
recipeIds: string[]
groups?: {
name: string
recipeIds: string[]
}[]
coverRecipeId?: string
createdAt: number
updatedAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/collections
A new collection, optionally with recipes and named groups.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | any JSON | Yes | |
description | any JSON | No | |
recipeIds | string[] | No | up to 2,000 items; each up to 40 characters |
groups | object[] | No | up to 50 items |
groups[].name | any JSON | Yes | |
groups[].recipeIds | string[] | Yes | up to 2,000 items; each up to 40 characters |
Also checked: Unknown fields are rejected.
Response 201
{
collection: {
collectionId: string
ownerId: string
name: string
description?: string
recipeIds: string[]
groups?: {
name: string
recipeIds: string[]
}[]
coverRecipeId?: string
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
PATCH /v1/me/food/collections/:collectionId
Renames a collection or changes its recipes (add, remove, or the whole recipeIds in order).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:collectionId | A Food collection's id (fcl_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | any JSON | No | |
description | any JSON | No | |
recipeIds | string[] | No | up to 2,000 items; each up to 40 characters |
groups | object[] | No | up to 50 items |
groups[].name | any JSON | Yes | |
groups[].recipeIds | string[] | Yes | up to 2,000 items; each up to 40 characters |
add | string[] | No | up to 500 items; each up to 40 characters |
remove | string[] | No | up to 500 items; each up to 40 characters |
Also checked: Unknown fields are rejected.
Response 200
{
collection: {
collectionId: string
ownerId: string
name: string
description?: string
recipeIds: string[]
groups?: {
name: string
recipeIds: string[]
}[]
coverRecipeId?: string
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
DELETE /v1/me/food/collections/:collectionId
Deletes a collection (its recipes stay).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:collectionId | A Food collection's id (fcl_…). |
Response 200
{
collectionId: string
deleted: boolean
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/plan
The household's meals between two days (at most 92).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Notes |
|---|---|---|---|
from | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
to | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
Response 200
{
entries: {
leftover?: {
itemId: string
cookedOn: string
}
entryId: string
homeId: string
day: string
meal: "breakfast" | "lunch" | "dinner" | "snack"
recipeId?: string
title: string
servings?: number
note?: string
addedBy: string
calendarEventId?: string
cooked?: boolean
createdAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/plan
Puts a meal on the household's plan: a recipe, or a note ("Leftovers").
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
day | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
meal | `` | Yes | |
recipeId | string | No | up to 40 characters |
title | any JSON | No | |
servings | integer | No | 1–100 |
note | any JSON | No | |
leftoverItemId | string | No | up to 40 characters |
Also checked: Unknown fields are rejected. Choose a recipe or write what's on.
Response 201
{
entry: {
leftover?: {
itemId: string
cookedOn: string
}
entryId: string
homeId: string
day: string
meal: "breakfast" | "lunch" | "dinner" | "snack"
recipeId?: string
title: string
servings?: number
note?: string
addedBy: string
calendarEventId?: string
cooked?: boolean
createdAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
PATCH /v1/me/food/plan/:entryId
Moves or changes a meal (from: the day it's on now, which saves a lookup).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:entryId | A meal on the plan (fpe_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
day | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
meal | "breakfast" | "lunch" | "dinner" | "snack" | No | |
recipeId | string | No | up to 40 characters |
title | string | No | up to 200 characters |
servings | integer | No | 1–100 |
note | string | No | up to 500 characters |
cooked | boolean | No | |
from | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
Also checked: Unknown fields are rejected.
Response 200
{
entry: {
leftover?: {
itemId: string
cookedOn: string
}
entryId: string
homeId: string
day: string
meal: "breakfast" | "lunch" | "dinner" | "snack"
recipeId?: string
title: string
servings?: number
note?: string
addedBy: string
calendarEventId?: string
cooked?: boolean
createdAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
DELETE /v1/me/food/plan/:entryId
Takes a meal off the plan (day: the day it's on, which saves a lookup).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:entryId | A meal on the plan (fpe_…). |
| Query parameter | Type | Required | Notes |
|---|---|---|---|
day | string | No |
Response 200
{
entry: {
leftover?: {
itemId: string
cookedOn: string
}
entryId: string
homeId: string
day: string
meal: "breakfast" | "lunch" | "dinner" | "snack"
recipeId?: string
title: string
servings?: number
note?: string
addedBy: string
calendarEventId?: string
cooked?: boolean
createdAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/plan/feed
The household's private calendar feed link (url, and webcal for calendar apps).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
feed: {
url: string
webcal: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/plan/feed/rotate
Replaces the calendar feed link; the old one stops working.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 200
{
feed: {
url: string
webcal: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/plan/suggest
Proposes (or with apply, plans) meals for the days ahead from the suggestions.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
from | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
days | integer | No | 1–14 |
meal | "breakfast" | "lunch" | "dinner" | "snack" | No | |
maxMinutes | integer | No | 5–600 |
diets | string[] | No | up to 10 items; each up to 40 characters |
budget | number | No | 1–100000 |
apply | boolean | No |
Also checked: Unknown fields are rejected.
Response 200
{
budget?: number
cost?: {
total: number
priced: number
nights: number
}
from: string
days: number
meal: "breakfast" | "lunch" | "dinner" | "snack"
applied: boolean
planned: {
cost?: number
day: string
meal: "breakfast" | "lunch" | "dinner" | "snack"
recipeId: string
title: string
totalMinutes?: number
reasons: string[]
}[]
skippedDays: string[]
unfilled: string[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/plan/calendar
Puts planned meals on a Mail calendar of the person's (events at meal times, linking back).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
orgId | string | Yes | 1–64 characters |
mailboxId | string | Yes | 1–64 characters |
calendarId | string | Yes | 1–64 characters |
from | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
to | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
timeZone | string | Yes | 1–64 characters |
Response 200
{
added: number
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
403 | You can't add events to calendars in that organization. |
404 | Mailbox not found |
GET /v1/me/food/lists
The household's shopping lists, most recently changed first.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
lists: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/lists
A new list: empty, from the plan between two days, or from recipes at given servings (merged by ingredient, by aisle, without the pantry).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | No | up to 100 characters |
fromPlan | object | No | |
fromPlan.from | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
fromPlan.to | string | Yes | matches ^\d{4}-\d{2}-\d{2}$ |
fromRecipes | object[] | No | up to 50 items |
fromRecipes[].recipeId | string | Yes | up to 40 characters |
fromRecipes[].servings | integer | No | 1–100 |
skipPantry | boolean | No | |
withStaples | boolean | No |
Also checked: Unknown fields are rejected.
Response 201
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/lists/:listId
A shopping list with its items (aisle, amount, ticked, the recipes they're for).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
Response 200
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
prices: {
[key: string]: {
day: string
paid: number
storeName: string
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
PATCH /v1/me/food/lists/:listId
Renames a list.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | 1–100 characters |
Response 200
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
DELETE /v1/me/food/lists/:listId
Deletes a list.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
Response 200
{
listId: string
deleted: boolean
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/lists/:listId/items
Adds items to a list (their aisle from the ingredient when not given).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
items | object[] | Yes | 1–100 items |
items[].name | any JSON | Yes | |
items[].quantity | number | No | 0–100000 |
items[].unit | `` | No | |
items[].amount | any JSON | No | |
items[].aisle | `` | No | |
items[].note | any JSON | No |
Also checked: Unknown fields are rejected.
Response 201
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/lists/:listId/recipes
Adds recipes' ingredients to a list (merged with what's on it).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
recipes | object[] | Yes | 1–50 items |
recipes[].recipeId | string | Yes | up to 40 characters |
recipes[].servings | integer | No | 1–100 |
skipPantry | boolean | No |
Also checked: Unknown fields are rejected.
Response 200
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
PATCH /v1/me/food/lists/:listId/items/:itemId
Ticks an item (checked) or changes it; two people ticking at once both land.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
:itemId | Drive file (fil_…) or folder (fld_…) id, or a drive id for its top folder; under me/food a shopping list item (fli_…) or something in the pantry (fpn_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | any JSON | No | |
quantity | number | No | 0–100000 |
unit | `` | No | |
amount | any JSON | No | |
aisle | `` | No | |
note | any JSON | No | |
checked | boolean | No |
Also checked: Unknown fields are rejected. Unknown fields are rejected.
Response 200
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/lists/:listId/items/remove
Removes items by id, or every ticked one ({ "checked": true }).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:listId | Saved map id (mls_…); under marketing a list (mkl_…); under me/food a shopping list (fls_…). |
Response 200
{
list: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/pantry
What the household has in, soonest use-by first.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
items: {
itemId: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
addedBy: string
addedAt: number
source?: "photo" | "manual" | "receipt" | "leftovers" | "barcode"
barcode?: string
leftover?: {
recipeId: string
title: string
portions: number
cookedOn: string
}
}[]
prices: {
[key: string]: {
day: string
paid: number
storeName: string
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/pantry
Adds things to the household's pantry, fridge or freezer: placed by kind and given an estimated use-by unless given; merged into the same thing already there unless merge is false.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
items | object[] | Yes | 1–100 items |
items[].name | any JSON | Yes | |
items[].quantity | number | No | 0–100000 |
items[].unit | `` | No | |
items[].place | "pantry" | "fridge" | "freezer" | No | |
items[].useBy | string | No | matches ^\d{4}-\d{2}-\d{2}$; can be null |
items[].ingredientId | string | No | up to 60 characters |
items[].source | "manual" | "barcode" | "photo" | "receipt" | "leftovers" | No | |
items[].barcode | string | No | matches ^\d{8,14}$ |
items[].merge | boolean | No |
Also checked: Unknown fields are rejected.
Response 201
{
items: {
itemId: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
addedBy: string
addedAt: number
source?: "photo" | "manual" | "receipt" | "leftovers" | "barcode"
barcode?: string
leftover?: {
recipeId: string
title: string
portions: number
cookedOn: string
}
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/pantry/use-soon
What needs using soon (within days, 3 by default) and the recipes that use the most of it.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Notes |
|---|---|---|---|
days | integer | No | 1–14; coerced from a string |
today | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
Response 200
{
items: {
itemId: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
addedBy: string
addedAt: number
source?: "photo" | "manual" | "receipt" | "leftovers" | "barcode"
barcode?: string
leftover?: {
recipeId: string
title: string
portions: number
cookedOn: string
}
}[]
recipes: {
recipe: {
createdAt: number
updatedAt: number
title: string
tags: string[]
ownerId: string
shared?: boolean
favourite?: boolean
recipeId: string
servings: number
totalMinutes?: number
cuisines: string[]
courses: string[]
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
diets: string[]
allergens: string[]
kcalPerServing?: number
ingredientIds: string[]
amounts?: {
[key: string]: {
g?: number
ml?: number
each?: number
minor?: true
}
}
measuredLines?: number
image?: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}
}
uses: string[]
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/pantry/barcodes/:code
What a barcode is (the household's own answer, else Open Food Facts), as a pantry item to check; found: false when nobody knows it.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:code | Mirage invite code (8 letters and digits); under me/food a household invitation's code (from its link). |
| Query parameter | Type | Required | Notes |
|---|---|---|---|
today | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
Response 200
{
code: string
found: true
source: "household"
item: {
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
barcode?: string
brand?: string
mergeInto?: {
itemId: string
name: string
}
}
} | {
code: string
found: false
source?: undefined
item?: undefined
} | {
code: string
found: true
source: "openfoodfacts"
item: {
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
barcode?: string
brand?: string
mergeInto?: {
itemId: string
name: string
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
PUT /v1/me/food/pantry/barcodes/:code
The household's own answer for a barcode (one nobody knew, or a correction).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:code | Mirage invite code (8 letters and digits); under me/food a household invitation's code (from its link). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | 1–120 characters; trimmed |
ingredientId | string | No | up to 60 characters |
quantity | number | No | 0–100000 |
unit | "g" | "kg" | "ml" | "l" | No |
Also checked: Unknown fields are rejected.
Response 200
{
barcode: {
ingredientId?: string
code: string
name: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/pantry/scans
Photos of a shelf, the fridge or a bench of shopping (base64, up to four), read by the model into items to check; poll the scan. Counts against the daily photo readings.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body (up to 10 MB)
| Field | Type | Required | Notes |
|---|---|---|---|
photos | string[] | Yes | 1–4 items; each 10–12,000,000 characters |
Response 202
{
scan: {
createdAt: number
error?: string
items?: {
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
barcode?: string
brand?: string
mergeInto?: {
itemId: string
name: string
}
}[]
scanId: string
status: "done" | "failed" | "running"
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
413 | That's too large. Send pictures of at most 8 MB, four at a time. |
GET /v1/me/food/pantry/scans/:scanId
A pantry photo being read: running, then done with the items to check (placed, with estimated use-bys and what each would add to), or failed.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:scanId | A pantry photo being read (fsc_…). |
Response 200
{
scan: {
createdAt: number
error?: string
items?: {
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
barcode?: string
brand?: string
mergeInto?: {
itemId: string
name: string
}
}[]
scanId: string
status: "done" | "failed" | "running"
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
PATCH /v1/me/food/pantry/:itemId
Changes something in the pantry (useBy: null clears its date).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:itemId | Drive file (fil_…) or folder (fld_…) id, or a drive id for its top folder; under me/food a shopping list item (fli_…) or something in the pantry (fpn_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | any JSON | No | |
quantity | number | No | 0–100000 |
unit | `` | No | |
place | "pantry" | "fridge" | "freezer" | No | |
useBy | string | No | matches ^\d{4}-\d{2}-\d{2}$; can be null |
Also checked: Unknown fields are rejected.
Response 200
{
item: {
itemId: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
addedBy: string
addedAt: number
source?: "photo" | "manual" | "receipt" | "leftovers" | "barcode"
barcode?: string
leftover?: {
recipeId: string
title: string
portions: number
cookedOn: string
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/pantry/remove
Takes things out of the pantry (used up).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
itemIds | string[] | Yes | 1–500 items; each 1–60 characters |
Response 200
{
removed: number
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/cookable
"What can I cook?": the person's and household's recipes by how much the pantry covers, with what's missing.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
recipes: {
recipe: {
createdAt: number
updatedAt: number
title: string
tags: string[]
ownerId: string
shared?: boolean
favourite?: boolean
recipeId: string
servings: number
totalMinutes?: number
cuisines: string[]
courses: string[]
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
diets: string[]
allergens: string[]
kcalPerServing?: number
ingredientIds: string[]
amounts?: {
[key: string]: {
g?: number
ml?: number
each?: number
minor?: true
}
}
measuredLines?: number
image?: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}
}
have: number
need: number
missing: string[]
expiring: string[]
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/receipts
The household's grocery receipts, newest purchase first (status: new, added or dismissed).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Notes |
|---|---|---|---|
status | "new" | "added" | "dismissed" | No |
Response 200
{
receipts: {
receiptId: string
store: string
storeName: string
purchasedOn: string
total?: number
lines: {
id: string
text: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
price?: number
detail?: string
food: boolean
place?: "fridge" | "freezer" | "pantry"
useBy?: string
}[]
status: "added" | "dismissed" | "new"
via: "upload" | "mail" | "forward"
read: "model" | "rules"
userId?: string
createdAt: number
addedAt?: number
addedBy?: string
added?: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/receipts/settings
Receipts settings: the household's forwarding address (when made), whether receipts go straight in, Gmail's last confirmation, and the person's Cactive Mail link.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
stores: {
id: string
name: string
}[]
mail?: {
orgId: string
mailboxId: string
address: string
since: number
backfilledAt?: number
}
confirmation?: {
code?: string
link?: string
at: number
}
autoAdd: boolean
forwarding?: string
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
PATCH /v1/me/food/receipts/settings
Whether the household's new receipts go straight into the pantry (autoAdd) or wait for review.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
autoAdd | boolean | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
home: {
receipts: {
autoAdd: boolean
token?: string
confirmation?: {
code?: string
link?: string
at: number
}
}
homeId: string
name: string
members: {
userId: string
name?: string
role: "owner" | "member"
joinedAt: number
}[]
createdAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/receipts/forwarding
Makes the household's private forwarding address (or a new one with rotate: the old one stops working).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
rotate | boolean | No |
Also checked: Unknown fields are rejected.
Response 200
{
address: string
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
PUT /v1/me/food/receipts/mail
Turns on grocery receipts from one of the person's Cactive Mail mailboxes (they must be a member): Food then reads only mail from the stores' e-receipt senders there.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
orgId | string | Yes | 1–64 characters |
mailboxId | string | Yes | 1–64 characters |
Also checked: Unknown fields are rejected.
Response 200
{
settings: {
measure: "metric" | "original" | "imperial"
spoons: "au" | "us"
defaultServings: number
diets: string[]
avoidAllergens: string[]
weeknightMinutes?: number
weatherSuggestions: boolean
healthLogging: boolean
calendar?: {
orgId: string
calendarId: string
}
weekStartsOn: number
receiptMail?: {
orgId: string
mailboxId: string
address: string
since: number
backfilledAt?: number
}
useSoonAlerts: boolean
voiceControl: boolean
weeklyBudget?: number
timeZone?: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
403 | You can't read mail in that organization. |
404 | Mailbox not found |
DELETE /v1/me/food/receipts/mail
Stops reading grocery receipts from the person's Cactive Mail; receipts already read stay.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 200
{
settings: {
measure: "metric" | "original" | "imperial"
spoons: "au" | "us"
defaultServings: number
diets: string[]
avoidAllergens: string[]
weeknightMinutes?: number
weatherSuggestions: boolean
healthLogging: boolean
calendar?: {
orgId: string
calendarId: string
}
weekStartsOn: number
receiptMail?: {
orgId: string
mailboxId: string
address: string
since: number
backfilledAt?: number
}
useSoonAlerts: boolean
voiceControl: boolean
weeklyBudget?: number
timeZone?: string
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/receipts/mail/backfill
"Check the last 30 days" (once): the linked mailbox's mail from the stores' e-receipt senders, read for receipts.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 202
{
queued: number
}Errors
| Status | Message |
|---|---|
400 | Turn on receipts from Cactive Mail first. |
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/receipts/:receiptId
One receipt (decrypted): the store, the day, the total and each line with its price, amount, place and estimated use-by.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:receiptId | A grocery receipt (frc_…). |
Response 200
{
receipt: {
receiptId: string
store: string
storeName: string
purchasedOn: string
total?: number
lines: {
id: string
text: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
price?: number
detail?: string
food: boolean
place?: "fridge" | "freezer" | "pantry"
useBy?: string
}[]
status: "added" | "dismissed" | "new"
via: "upload" | "mail" | "forward"
read: "model" | "rules"
userId?: string
createdAt: number
addedAt?: number
addedBy?: string
added?: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/receipts/:receiptId/add
Puts a receipt into the pantry: its food lines, or the lines as ticked and changed (merged into what's there).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:receiptId | A grocery receipt (frc_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
lines | object[] | No | up to 500 items |
lines[].id | string | Yes | up to 40 characters |
lines[].include | boolean | No | |
lines[].name | string | No | 1–200 characters; trimmed |
lines[].quantity | number | No | 0–100000; can be null |
lines[].unit | any JSON | No | can be null |
lines[].place | "pantry" | "fridge" | "freezer" | No | |
lines[].useBy | string | No | matches ^\d{4}-\d{2}-\d{2}$; can be null |
lines[].merge | boolean | No |
Also checked: Unknown fields are rejected.
Response 200
{
receipt: {
receiptId: string
store: string
storeName: string
purchasedOn: string
total?: number
lines: {
id: string
text: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
price?: number
detail?: string
food: boolean
place?: "fridge" | "freezer" | "pantry"
useBy?: string
}[]
status: "added" | "dismissed" | "new"
via: "upload" | "mail" | "forward"
read: "model" | "rules"
userId?: string
createdAt: number
addedAt?: number
addedBy?: string
added?: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/receipts/:receiptId/dismiss
Leaves a receipt out of the pantry (it still counts in spending).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:receiptId | A grocery receipt (frc_…). |
Response 200
{
receipt: {
status: "dismissed"
receiptId: string
store: string
storeName: string
purchasedOn: string
total?: number
lines: {
id: string
text: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
price?: number
detail?: string
food: boolean
place?: "fridge" | "freezer" | "pantry"
useBy?: string
}[]
via: "upload" | "mail" | "forward"
read: "model" | "rules"
userId?: string
createdAt: number
addedAt?: number
addedBy?: string
added?: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
DELETE /v1/me/food/receipts
Deletes the household's receipts and prices (and the key they're sealed with).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 200
{
removed: number
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/spending
Grocery spending by month from the household's receipts (months: 1 to 24, 6 by default).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Notes |
|---|---|---|---|
months | integer | No | 1–24; coerced from a string |
today | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
Response 200
{
months: {
month: string
total: number
receipts: number
stores: {
name: string
total: number
}[]
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/delete-all
Deletes all the person's Food data (call again while done is false): see docs/food/README.md → Privacy.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Response 200
{
done: boolean
remaining: number
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/suggestions
Ideas from the person's recipes with why (ratings, history, the season, the pantry, the weather when turned on), within their diets and allergens.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
maxMinutes | string | No | "" | |
course | string | No |
Response 200
{
suggestions: {
score: number
reasons: string[]
recipe: {
createdAt: number
updatedAt: number
title: string
tags: string[]
ownerId: string
shared?: boolean
favourite?: boolean
recipeId: string
servings: number
totalMinutes?: number
cuisines: string[]
courses: string[]
rating?: number
household: boolean
cookedCount: number
lastCookedAt?: number
diets: string[]
allergens: string[]
kcalPerServing?: number
ingredientIds: string[]
amounts?: {
[key: string]: {
g?: number
ml?: number
each?: number
minor?: true
}
}
measuredLines?: number
image?: {
imageId: string
versionId?: string
mime?: string
width?: number
height?: number
alt?: string
url: string
thumbUrl: string
}
}
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/substitutes
Substitutes for an ingredient from Food's kitchen notes, with amounts (none on file: an empty list and a note).
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Query parameter | Type | Required | Notes |
|---|---|---|---|
ingredient | string | Yes | 1–100 characters |
Response 200
{
note?: string
ingredient: string
substitutes: {
use: string
note?: string
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/widget
The phone's widgets: tonight's dinner and the first list's next items.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
today: string
tonight: null | {
title: string
meal: string
recipeId?: string
minutes?: number
imageUrl?: string
}
list: null | {
listId: string
name: string
remaining: number
items: {
itemId: string
name: string
amount: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
}[]
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
POST /v1/me/food/imports/url
A web page's recipe as a draft to check (schema.org data, else its lists); a page without either is read by the model as an import.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | Yes | 4–2,000 characters |
Response 200
{
draft?: {
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags?: string[]
steps?: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
publicToken?: string
servings?: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients?: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines?: string[]
courses?: string[]
claimedDiets?: string[]
difficulty?: "easy" | "medium" | "hard"
spoons?: "au" | "us"
notes?: string
rating?: number
household?: boolean
lastCookedAt?: number
imageUrls?: string[]
}
from?: string
import?: {
importId: string
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
status: "done" | "failed" | "running"
label: string
recipeIds: string[]
skipped: number
error?: string
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/imports/text
Recipes in pasted text as drafts to check (several when separated by a line of dashes).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
text | string | Yes | 1–100,000 characters |
Response 200
{
drafts: {
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags?: string[]
steps?: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
publicToken?: string
servings?: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients?: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines?: string[]
courses?: string[]
claimedDiets?: string[]
difficulty?: "easy" | "medium" | "hard"
spoons?: "au" | "us"
notes?: string
rating?: number
household?: boolean
lastCookedAt?: number
imageUrls?: string[]
images?: string[]
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/imports/photos
Photos or screenshots of one recipe (base64, up to four), read by the model; poll the import for its draft.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body (up to 10 MB)
| Field | Type | Required | Notes |
|---|---|---|---|
photos | string[] | Yes | 1–4 items; each 10–12,000,000 characters |
Response 202
{
import: {
importId: string
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
status: "done" | "failed" | "running"
label: string
recipeIds: string[]
skipped: number
error?: string
createdAt: number
updatedAt: number
draft?: {
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags?: string[]
steps?: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
publicToken?: string
servings?: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients?: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines?: string[]
courses?: string[]
claimedDiets?: string[]
difficulty?: "easy" | "medium" | "hard"
spoons?: "au" | "us"
notes?: string
rating?: number
household?: boolean
lastCookedAt?: number
imageUrls?: string[]
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
413 | That's too large. Send pictures of at most 8 MB, four at a time. |
POST /v1/me/food/imports/files
Starts importing another app's export: a link to PUT the file to, then complete.
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | 1–200 characters |
size | integer | Yes | ≥ 1 |
Response 201
{
importId: string
uploadUrl: string
contentType: string
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
POST /v1/me/food/imports/:importId/complete
The file has been sent: the worker reads it and saves its recipes (and Melt's cookbooks as collections).
Auth: user access token or platform agent key · Scope: food:write (when actor.type === "user")
| Path parameter | Description |
|---|---|
:importId | Takeout import id (imp_…); under me/food a Food import (fim_…). |
Response 202
{
import: {
importId: string
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
status: "done" | "failed" | "running"
label: string
recipeIds: string[]
skipped: number
error?: string
createdAt: number
updatedAt: number
draft?: {
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags?: string[]
steps?: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
publicToken?: string
servings?: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients?: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines?: string[]
courses?: string[]
claimedDiets?: string[]
difficulty?: "easy" | "medium" | "hard"
spoons?: "au" | "us"
notes?: string
rating?: number
household?: boolean
lastCookedAt?: number
imageUrls?: string[]
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:write on a key made by a person. |
GET /v1/me/food/imports
Your imports from the last two weeks: what they read, saved and skipped, or why they failed.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
imports: {
importId: string
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
status: "done" | "failed" | "running"
label: string
recipeIds: string[]
skipped: number
error?: string
createdAt: number
updatedAt: number
draft?: {
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags?: string[]
steps?: {
id: string
section?: string
text: string
timers?: object[]
}[]
favourite?: boolean
publicToken?: string
servings?: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients?: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines?: string[]
courses?: string[]
claimedDiets?: string[]
difficulty?: "easy" | "medium" | "hard"
spoons?: "au" | "us"
notes?: string
rating?: number
household?: boolean
lastCookedAt?: number
imageUrls?: string[]
}
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/imports/:importId
One import: its status and, for one recipe read from photos or a page, the draft to check.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
| Path parameter | Description |
|---|---|
:importId | Takeout import id (imp_…); under me/food a Food import (fim_…). |
Response 200
{
import: {
importId: string
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
status: "done" | "failed" | "running"
label: string
recipeIds: string[]
skipped: number
error?: string
createdAt: number
updatedAt: number
draft?: {
title: string
description?: string
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
tags?: string[]
steps?: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
favourite?: boolean
publicToken?: string
servings?: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients?: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
cuisines?: string[]
courses?: string[]
claimedDiets?: string[]
difficulty?: "easy" | "medium" | "hard"
spoons?: "au" | "us"
notes?: string
rating?: number
household?: boolean
lastCookedAt?: number
imageUrls?: string[]
}
}
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |
GET /v1/me/food/export
Everything of yours in Food as a file Food imports again (format: cactive-food): recipes, collections, settings and history. Pictures are links valid for an hour.
Auth: user access token or platform agent key · Scope: food:read (when actor.type === "user")
Response 200
{
format: string
version: number
exportedAt: string
settings: {
measure: "metric" | "original" | "imperial"
spoons: "au" | "us"
defaultServings: number
diets: string[]
avoidAllergens: string[]
weeknightMinutes?: number
weatherSuggestions: boolean
healthLogging: boolean
calendar?: {
orgId: string
calendarId: string
}
weekStartsOn: number
receiptMail?: {
orgId: string
mailboxId: string
address: string
since: number
backfilledAt?: number
}
useSoonAlerts: boolean
voiceControl: boolean
weeklyBudget?: number
timeZone?: string
}
recipes: {
imageUrls?: string[]
recipeId: string
ownerId: string
title: string
description?: string
servings: number
yieldLabel?: string
prepMinutes?: number
cookMinutes?: number
totalMinutes?: number
ingredients: {
id: string
section?: string
quantity?: number
quantityMax?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
item: string
note?: string
ingredientId?: string
optional?: boolean
}[]
steps: {
id: string
section?: string
text: string
timers?: {
seconds: number
label?: string
}[]
}[]
cuisines: string[]
courses: string[]
claimedDiets: string[]
tags: string[]
difficulty?: "easy" | "medium" | "hard"
spoons: "au" | "us"
source?: {
kind: "text" | "photo" | "web" | "manual" | "melt" | "paprika" | "mela" | "crouton" | "copymethat" | "assistant" | "copy"
url?: string
name?: string
author?: string
}
notes?: string
rating?: number
favourite?: boolean
household: boolean
cookedCount: number
lastCookedAt?: number
createdAt: number
updatedAt: number
version: number
}[]
collections: {
name: string
description?: string
recipeIds: string[]
}[]
household: {
name: string
members: {
name?: string
role: "owner" | "member"
joinedAt: string
}[]
plan: {
[key: string]: unknown
}[]
lists: {
listId: string
homeId: string
name: string
items: {
itemId: string
name: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
amount?: string
aisle: "none" | "other" | "household" | "meat" | "dairy" | "breakfast" | "baking" | "produce" | "bakery" | "seafood" | "deli" | "chilled" | "pasta" | "canned" | "condiments" | "oils" | "spices" | "nuts" | "spreads" | "frozen" | "drinks"
ingredientId?: string
note?: string
checked: boolean
forRecipes?: string[]
addedBy: string
addedAt: number
checkedBy?: string
}[]
createdAt: number
updatedAt: number
version: number
}[]
pantry: {
itemId: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
place: "fridge" | "freezer" | "pantry"
useBy?: string
addedBy: string
addedAt: number
source?: "photo" | "manual" | "receipt" | "leftovers" | "barcode"
barcode?: string
leftover?: {
recipeId: string
title: string
portions: number
cookedOn: string
}
}[]
receipts: {
receiptId: string
store: string
storeName: string
purchasedOn: string
total?: number
lines: {
id: string
text: string
name: string
ingredientId?: string
quantity?: number
unit?: "slice" | "g" | "kg" | "mg" | "oz" | "lb" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "floz" | "pint" | "quart" | "pinch" | "dash" | "piece" | "clove" | "can" | "jar" | "bunch" | "handful" | "sprig" | "sheet" | "packet" | "stick" | "head" | "fillet" | "rasher" | "leaf" | "stalk"
price?: number
detail?: string
food: boolean
place?: "fridge" | "freezer" | "pantry"
useBy?: string
}[]
status: "added" | "dismissed" | "new"
via: "upload" | "mail" | "forward"
read: "model" | "rules"
userId?: string
createdAt: number
addedAt?: number
addedBy?: string
added?: number
}[]
prices: {
key: string
ingredientId?: string
name: string
paid: number
perG?: number
perMl?: number
perEach?: number
store: string
storeName: string
day: string
}[]
}
cooked: {
note?: string
rating?: number
recipeId: string
title: string
at: string
}[]
}Errors
| Status | Message |
|---|---|
403 | Food isn't available to the phone assistant. |
403 | Food is reachable from the Food and phone apps. |
403 | Food needs food:read on a key made by a person. |