POST/api/v1/households/{householdId}/imports/purchase-document
Upload a receipt, order or shelf photo and have Bryti read it into lines
Adults only; a Junior member gets 403 (#981, ADR-083). JSON body: pasted text, or a base64 PDF or image — 10 MB in all, a photo in at most four pages (#385). Purpose=StorageSnapshot for a photo of a fridge, freezer or pantry. Read before the call returns, and costs credits; a provider failure answers 200 with Status=Failed and the import stays retryable. Nothing reaches the pantry until confirm.Send an Idempotency-Key header: a retry with the same key answers 409 with the original operation's id instead of charging twice. 429 when the household's monthly AI quota is used up, or AI is paused for the month (ADR-023) — the latter with a Retry-After header.
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too Many Requests
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
-X POST \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/purchase-document" \
-H "Content-Type: application/json" \
-d '{"fileName":"string","contentType":"string","text":"string","base64Data":"string","purpose":"Purchase"}'
Example response
200application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"householdId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "string",
"documentKind": "Text",
"purpose": "Purchase",
"snapshotLocation": "Fridge",
"status": "Pending",
"lines": [
{
"rawText": "string",
"foodKey": "string",
"displayName": "string",
"quantity": 0,
"unit": "string",
"storageLocation": {},
"confidence": 0,
"needsAttention": false,
"asksForDate": false
}
],
"error": "string",
"createdAt": "2026-09-07T16:30:00Z",
"confirmedAt": "2026-09-07T16:30:00Z"
}
POST/api/v1/households/{householdId}/imports/recipe-photo
Import a recipe from one or more photos into the household's library
Adults only; a Junior member gets 403 (ADR-083). JSON body with base64 images — at most four pages of the same recipe, 10 MB in all (#174). Costs credits. One shot, no review: Status is Saved, AlreadyExists (the existing recipe rides back), Partial (what was read, for the editor to finish — nothing saved, ADR-152) or Failed with a Failure kind. ConversationId has the result told in that chat thread (ADR-141); HostRecipeId + ReferenceId save it as a base recipe linked into that host (ADR-154 pt. 7).Send an Idempotency-Key header: a retry with the same key answers 409 instead of charging twice. 429 when the AI quota is used up, or AI is paused for the month (ADR-023) — the latter with a Retry-After header.
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too Many Requests
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
-X POST \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/recipe-photo" \
-H "Content-Type: application/json" \
-d '{"fileName":"string","contentType":"string","base64Data":"string"}'
Example response
200application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "Saved",
"recipe": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"category": {},
"source": {},
"ingredients": [
{}
],
"steps": [
"string"
],
"tags": [
"string"
],
"activeMinutes": 0,
"totalMinutes": 0,
"basePortions": 0,
"isFamilyFavorite": false,
"favoriteMemberIds": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"timesCooked": 0,
"excludedFromPlanning": false,
"isBase": false,
"isDinner": true,
"isCore": false
},
"error": "string",
"createdAt": "2026-09-07T16:30:00Z"
}
POST/api/v1/households/{householdId}/imports/recipe-url
Import a recipe from a web page into the household's library
Adults only; a Junior member gets 403 (ADR-083). The server fetches the page itself — public addresses only; an internal or malformed one is a 400 — and asks Bryti to read it, which costs credits. A page that cannot be fetched, sits behind a login wall or paywall, times out or is a file answers 200 with Status=Failed and a Failure kind, not an error (#1712). Otherwise as recipe-photo: Saved, AlreadyExists or Partial, with ConversationId and HostRecipeId + ReferenceId.Send an Idempotency-Key header: a retry with the same key answers 409 instead of charging twice. 429 when the AI quota is used up, or AI is paused for the month (ADR-023) — the latter with a Retry-After header.
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too Many Requests
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
-X POST \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/recipe-url" \
-H "Content-Type: application/json" \
-d '{"url":"string"}'
Example response
200application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "Saved",
"recipe": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"category": {},
"source": {},
"ingredients": [
{}
],
"steps": [
"string"
],
"tags": [
"string"
],
"activeMinutes": 0,
"totalMinutes": 0,
"basePortions": 0,
"isFamilyFavorite": false,
"favoriteMemberIds": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"timesCooked": 0,
"excludedFromPlanning": false,
"isBase": false,
"isDinner": true,
"isCore": false
},
"error": "string",
"createdAt": "2026-09-07T16:30:00Z"
}
POST/api/v1/households/{householdId}/imports/recipe-text
Import a recipe from pasted text into the household's library
Adults only; a Junior member gets 403 (ADR-083). Up to 200 kB of text (#982); costs credits. Same outcomes and options as recipe-photo: Saved, AlreadyExists, Partial or Failed, with ConversationId and HostRecipeId + ReferenceId. A page the phone read with the member's own sign-in (#3818) may name its SourceUrl and ImageUrl: the source is kept on the recipe and the picture fetched as the recipe-url door fetches one; either must be an http(s) address or the call is a 400. An Idempotency-Key header is honoured here too — a retry with the same key answers 409. 429 when the AI quota is used up, or AI is paused for the month (ADR-023) — the latter with a Retry-After header.
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too Many Requests
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
-X POST \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/recipe-text" \
-H "Content-Type: application/json" \
-d '{"text":"string"}'
Example response
200application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "Saved",
"recipe": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"category": {},
"source": {},
"ingredients": [
{}
],
"steps": [
"string"
],
"tags": [
"string"
],
"activeMinutes": 0,
"totalMinutes": 0,
"basePortions": 0,
"isFamilyFavorite": false,
"favoriteMemberIds": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"timesCooked": 0,
"excludedFromPlanning": false,
"isBase": false,
"isDinner": true,
"isCore": false
},
"error": "string",
"createdAt": "2026-09-07T16:30:00Z"
}
GET/api/v1/households/{householdId}/imports/recipe/{importId}
Read a recipe import back: how the reading went, and the recipe
Adults only — the whole import flow is, its status included (ADR-083). The three recipe-import routes normally answer with the finished reading, but since ADR-189 (#2283) the row is written and committed before the model is asked, so they may also answer Pending: the import exists, it is paid for, and it will finish even if the API restarts first. This is where a client waits for that — poll it until Status is Saved, AlreadyExists, Partial or Failed.Two fields are only ever on the answer the reading itself produced: a Partial import's half recipe and the «er det denne?» link suggestions live in the API instance that read it, not on the row, so an import read back after a restart has its status and its recipe but not those (#307, US-1813).
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/recipe/3fa85f64-5717-4562-b3fc-2c963f66afa6"
Example response
200application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "Saved",
"recipe": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"category": {},
"source": {},
"ingredients": [
{}
],
"steps": [
"string"
],
"tags": [
"string"
],
"activeMinutes": 0,
"totalMinutes": 0,
"basePortions": 0,
"isFamilyFavorite": false,
"favoriteMemberIds": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"timesCooked": 0,
"excludedFromPlanning": false,
"isBase": false,
"isDinner": true,
"isCore": false
},
"error": "string",
"createdAt": "2026-09-07T16:30:00Z"
}
GET/api/v1/households/{householdId}/imports/recipe/page
Read the page question still open about a recipe, if there is one
Adults only, like the rest of the import flow (ADR-083). A photographed page with more than one recipe on it is asked about once the import has saved one of them (#3308); this is where the recipe page finds that question again after a reload or a closed app (#3322): the import to answer and the reading, by the saved recipe's id. 204 when nothing is left to ask — never asked, already answered, or not this household's.
Responses
- 204No Content
- 400The request could not be read as the route needs it: a body that is not the documented shape (a required field left out, a null where none is allowed, a value too long for its field), a parameter that does not parse, or a NUL character anywhere (#3540, #3541, #3543). Code validation_failed or value_too_long.
application/problem+jsonProblemDetails- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/recipe/page"
Example response
200application/json
{
"importId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"page": {
"relation": {},
"reason": "string",
"titles": [
"string"
],
"oneDishTitle": "string",
"savedAsOneDish": false
}
}
POST/api/v1/households/{householdId}/imports/recipe/{importId}/page
Answer what the recipes on one photographed page are to each other
Adults only, like the rest of the import flow (ADR-083). A recipe import whose answer carried Page read more than one recipe on the page and saved one reading of it (#3308). OneDish keeps them as one recipe with parts (ADR-154); Only with Index keeps that dish alone; All saves every dish as a recipe of its own, each told in the chat thread the import came from (ADR-141). The saved recipe is rewritten in place, never deleted. No credits: nothing is read again. 400 once the page has been answered, or for an index that is not a dish on it.
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
-X POST \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/recipe/3fa85f64-5717-4562-b3fc-2c963f66afa6/page" \
-H "Content-Type: application/json" \
-d '{"choice":"OneDish"}'
Example response
200application/json
{
"recipes": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"category": {},
"source": {},
"ingredients": [
{}
],
"steps": [
"string"
],
"tags": [
"string"
],
"activeMinutes": 0,
"totalMinutes": 0,
"basePortions": 0,
"isFamilyFavorite": false,
"favoriteMemberIds": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"timesCooked": 0,
"excludedFromPlanning": false,
"isBase": false,
"isDinner": true,
"isCore": false
}
]
}
POST/api/v1/households/{householdId}/imports/{importId}/confirm
Confirm the reviewed lines and write them into the pantry
Adults only; a Junior member gets 403 (ADR-083). The only path from an import to the pantry: each kept line becomes a purchase observation — a sighting for a storage snapshot — dated by the receipt's own purchase time rather than today (#385), and each priced receipt line a price observation. StoreName, ChainCode and PurchasedAt override what was read. No credits. Not repeatable: 400 with no lines, and 400 on an import that is not in Parsed state, an already confirmed one included. With a ConversationId — the import was started from the chat's «+» — the same save writes one assistant turn in that thread saying how many things went in, with a pantry_import widget; an id that is not this household's is ignored, never a 403 (ADR-375 (#3305), ADR-141).
Responses
- 401No session, or the session is no longer valid.
application/problem+jsonProblemDetails- 426The build named in X-App-Version is below its platform's version floor (ADR-070). Problem type app-version-unsupported; the only way forward is the store. Callers without the header are never turned away.
application/problem+jsonProblemDetails- 429Too many calls in the window; Retry-After says how long.
application/problem+jsonProblemDetails- Headers
Retry-After- — Seconds to wait when a rate limit answered, or an HTTP date when the household's AI is paused. Absent when the household's AI quota for the period is used up — that resets by itself.
Example
curl -b "bryti_session=…" \
-X POST \
"https://bryti.app/api/v1/households/3fa85f64-5717-4562-b3fc-2c963f66afa6/imports/3fa85f64-5717-4562-b3fc-2c963f66afa6/confirm" \
-H "Content-Type: application/json" \
-d '{"lines":[{"foodKey":"string","displayName":"string","quantity":0,"unit":"string","storageLocation":{}}]}'
Example response
200application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"householdId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "string",
"documentKind": "Text",
"purpose": "Purchase",
"snapshotLocation": "Fridge",
"status": "Pending",
"lines": [
{
"rawText": "string",
"foodKey": "string",
"displayName": "string",
"quantity": 0,
"unit": "string",
"storageLocation": {},
"confidence": 0,
"needsAttention": false,
"asksForDate": false
}
],
"error": "string",
"createdAt": "2026-09-07T16:30:00Z",
"confirmedAt": "2026-09-07T16:30:00Z"
}