Journal dashboard widgets and quick-add; rich-text quick-add dialogs. Dashboard draft sync for live edit previews; assistant bubble + API tools. Journal UX: stress slider, mood grid, query cap fix.
1206 lines
34 KiB
YAML
1206 lines
34 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: famapp API
|
|
version: "1.0.0"
|
|
description: |
|
|
Household-scoped REST JSON API for famapp (fam.ginnoir.com).
|
|
|
|
Authenticate with an Auth.js session cookie (browser) or a household API bearer token
|
|
(external clients). Generate bearer tokens in Settings → API tokens.
|
|
|
|
servers:
|
|
- url: https://fam.ginnoir.com
|
|
description: Production
|
|
- url: http://localhost:3000
|
|
description: Local development
|
|
|
|
security:
|
|
- bearerAuth: []
|
|
- sessionCookie: []
|
|
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
description: Household API token from Settings → API tokens
|
|
sessionCookie:
|
|
type: apiKey
|
|
in: cookie
|
|
name: authjs.session-token
|
|
description: Auth.js session cookie (browser login)
|
|
|
|
schemas:
|
|
Error:
|
|
type: object
|
|
required: [error]
|
|
properties:
|
|
error:
|
|
type: string
|
|
|
|
OkResponse:
|
|
type: object
|
|
required: [ok]
|
|
properties:
|
|
ok:
|
|
type: boolean
|
|
enum: [true]
|
|
|
|
Calendar:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
name: { type: string }
|
|
color: { type: string, nullable: true }
|
|
visibility: { type: string, enum: [private, household] }
|
|
ownerId: { type: string, format: uuid }
|
|
|
|
CalendarInput:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name: { type: string }
|
|
color: { type: string, nullable: true }
|
|
visibility: { type: string, enum: [private, household], default: household }
|
|
|
|
CalendarEvent:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
calendarId: { type: string, format: uuid }
|
|
title: { type: string }
|
|
startAt: { type: string, format: date-time }
|
|
endAt: { type: string, format: date-time }
|
|
allDay: { type: boolean }
|
|
location: { type: string, nullable: true }
|
|
notes: { type: string, nullable: true }
|
|
|
|
EventInput:
|
|
type: object
|
|
required: [calendarId, title, startAt, endAt]
|
|
properties:
|
|
calendarId: { type: string, format: uuid }
|
|
title: { type: string }
|
|
startAt: { type: string, format: date-time }
|
|
endAt: { type: string, format: date-time }
|
|
allDay: { type: boolean, default: false }
|
|
location: { type: string, nullable: true }
|
|
notes: { type: string, nullable: true }
|
|
remindMinutesBefore: { type: integer, nullable: true }
|
|
|
|
ListSummary:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
type: { type: string }
|
|
name: { type: string }
|
|
archived: { type: boolean }
|
|
openCount: { type: integer }
|
|
doneCount: { type: integer }
|
|
createdAt: { type: string, format: date-time }
|
|
|
|
ListInput:
|
|
type: object
|
|
required: [type, name]
|
|
properties:
|
|
type: { type: string }
|
|
name: { type: string }
|
|
|
|
ListItem:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
listId: { type: string, format: uuid }
|
|
text: { type: string }
|
|
qty: { type: string, nullable: true }
|
|
notes: { type: string, nullable: true }
|
|
done: { type: boolean }
|
|
dueAt: { type: string, format: date-time, nullable: true }
|
|
assigneeId: { type: string, format: uuid, nullable: true }
|
|
position: { type: integer }
|
|
|
|
ItemInput:
|
|
type: object
|
|
required: [text]
|
|
properties:
|
|
text: { type: string }
|
|
qty: { type: string, nullable: true }
|
|
notes: { type: string, nullable: true }
|
|
dueAt: { type: string, format: date-time, nullable: true }
|
|
assigneeId: { type: string, format: uuid, nullable: true }
|
|
metadata: { type: object, nullable: true }
|
|
|
|
Note:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
householdId: { type: string, format: uuid }
|
|
authorId: { type: string, format: uuid }
|
|
title: { type: string }
|
|
body: { type: string }
|
|
pinned: { type: boolean }
|
|
remindAt: { type: string, format: date-time, nullable: true }
|
|
createdAt: { type: string, format: date-time }
|
|
updatedAt: { type: string, format: date-time }
|
|
|
|
NoteInput:
|
|
type: object
|
|
required: [title]
|
|
properties:
|
|
title: { type: string }
|
|
body: { type: string, default: "" }
|
|
pinned: { type: boolean, default: false }
|
|
remindAt: { type: string, format: date-time, nullable: true }
|
|
|
|
JournalEntry:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
householdId: { type: string, format: uuid }
|
|
userId: { type: string, format: uuid }
|
|
recordedAt: { type: string, format: date-time }
|
|
title: { type: string, nullable: true }
|
|
body: { type: string }
|
|
moods: { type: array, items: { type: string } }
|
|
stress: { type: integer, minimum: 1, maximum: 10, nullable: true }
|
|
pillsTaken: { type: boolean, nullable: true }
|
|
createdAt: { type: string, format: date-time }
|
|
updatedAt: { type: string, format: date-time }
|
|
|
|
JournalEntryInput:
|
|
type: object
|
|
required: [recordedAt]
|
|
properties:
|
|
recordedAt: { type: string, format: date-time }
|
|
title: { type: string, nullable: true }
|
|
body: { type: string, default: "" }
|
|
moods: { type: array, items: { type: string }, default: [] }
|
|
stress: { type: integer, minimum: 1, maximum: 10, nullable: true }
|
|
pillsTaken: { type: boolean, nullable: true }
|
|
|
|
GardenContainer:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
householdId: { type: string, format: uuid }
|
|
name: { type: string }
|
|
type: { type: string }
|
|
locationNotes: { type: string, nullable: true }
|
|
coverImageUrl: { type: string, nullable: true }
|
|
images: { type: array, items: { type: string } }
|
|
plantCount: { type: integer }
|
|
createdAt: { type: string, format: date-time }
|
|
updatedAt: { type: string, format: date-time }
|
|
|
|
GardenContainerDetail:
|
|
allOf:
|
|
- $ref: "#/components/schemas/GardenContainer"
|
|
- type: object
|
|
properties:
|
|
plants:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/PlantSummary"
|
|
|
|
ContainerInput:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name: { type: string }
|
|
type: { type: string, default: other }
|
|
locationNotes: { type: string, nullable: true }
|
|
coverImageUrl: { type: string, nullable: true }
|
|
|
|
PlantSummary:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
name: { type: string }
|
|
scientificName: { type: string, nullable: true }
|
|
healthStatus: { type: string }
|
|
primaryImageUrl: { type: string, nullable: true }
|
|
category: { type: string }
|
|
|
|
PlantListItem:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
containerId: { type: string, format: uuid, nullable: true }
|
|
containerName: { type: string, nullable: true }
|
|
name: { type: string }
|
|
healthStatus: { type: string }
|
|
primaryImageUrl: { type: string, nullable: true }
|
|
category: { type: string }
|
|
lastWateredAt: { type: string, format: date-time, nullable: true }
|
|
hasOverdueCare: { type: boolean }
|
|
|
|
PlantDetail:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
householdId: { type: string, format: uuid }
|
|
containerId: { type: string, format: uuid, nullable: true }
|
|
containerName: { type: string, nullable: true }
|
|
name: { type: string }
|
|
scientificName: { type: string, nullable: true }
|
|
speciesId: { type: string, nullable: true }
|
|
category: { type: string }
|
|
notes: { type: string, nullable: true }
|
|
acquisitionDate: { type: string, nullable: true }
|
|
growthStage: { type: string, nullable: true }
|
|
healthStatus: { type: string }
|
|
sunlight: { type: string, nullable: true }
|
|
wateringNotes: { type: string, nullable: true }
|
|
fertilizingNotes: { type: string, nullable: true }
|
|
primaryImageUrl: { type: string, nullable: true }
|
|
images: { type: array, items: { type: string } }
|
|
createdAt: { type: string, format: date-time }
|
|
updatedAt: { type: string, format: date-time }
|
|
|
|
PlantInput:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name: { type: string }
|
|
category: { type: string, default: other }
|
|
containerId: { type: string, format: uuid, nullable: true }
|
|
healthStatus: { type: string, default: healthy }
|
|
growthStage: { type: string, nullable: true }
|
|
scientificName: { type: string, nullable: true }
|
|
speciesId: { type: string, nullable: true }
|
|
sunlight: { type: string, nullable: true }
|
|
wateringNotes: { type: string, nullable: true }
|
|
fertilizingNotes: { type: string, nullable: true }
|
|
notes: { type: string, nullable: true }
|
|
acquisitionDate: { type: string, nullable: true }
|
|
images: { type: array, items: { type: string }, default: [] }
|
|
primaryImageUrl: { type: string, nullable: true }
|
|
|
|
BangStats:
|
|
type: object
|
|
properties:
|
|
total: { type: integer }
|
|
recent:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/BangSummary"
|
|
|
|
BangSummary:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
occurredOn: { type: string, format: date, description: YYYY-MM-DD }
|
|
recordedByName: { type: string, nullable: true }
|
|
|
|
Bang:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
occurredOn: { type: string, format: date }
|
|
recordedBy: { type: string, format: uuid, nullable: true }
|
|
createdAt: { type: string, format: date-time }
|
|
|
|
BangInput:
|
|
type: object
|
|
properties:
|
|
occurredOn:
|
|
type: string
|
|
format: date
|
|
description: YYYY-MM-DD; defaults to today
|
|
|
|
BangUpdateInput:
|
|
type: object
|
|
required: [occurredOn]
|
|
properties:
|
|
occurredOn: { type: string, format: date }
|
|
|
|
CareLog:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
careType: { type: string }
|
|
notes: { type: string, nullable: true }
|
|
performedAt: { type: string, format: date-time }
|
|
performedBy: { type: string, format: uuid, nullable: true }
|
|
|
|
CareLogInput:
|
|
type: object
|
|
required: [careType]
|
|
properties:
|
|
careType: { type: string }
|
|
notes: { type: string, nullable: true }
|
|
performedAt: { type: string, format: date-time }
|
|
|
|
CareSchedule:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
careType: { type: string }
|
|
intervalDays: { type: integer }
|
|
lastPerformedAt: { type: string, format: date-time, nullable: true }
|
|
nextDueAt: { type: string, format: date-time, nullable: true }
|
|
enabled: { type: boolean }
|
|
daysUntilDue: { type: integer, nullable: true }
|
|
isOverdue: { type: boolean }
|
|
|
|
CareScheduleInput:
|
|
type: object
|
|
required: [careType, intervalDays]
|
|
properties:
|
|
careType: { type: string }
|
|
intervalDays: { type: integer, minimum: 1, maximum: 365 }
|
|
enabled: { type: boolean }
|
|
|
|
CareScheduleToggleInput:
|
|
type: object
|
|
required: [enabled]
|
|
properties:
|
|
enabled: { type: boolean }
|
|
|
|
ScheduleOnCalendarInput:
|
|
type: object
|
|
required: [calendarId]
|
|
properties:
|
|
calendarId: { type: string, format: uuid }
|
|
reminderMinutesBefore: { type: integer, nullable: true }
|
|
|
|
ShareableEntityType:
|
|
type: object
|
|
properties:
|
|
type: { type: string }
|
|
label: { type: string }
|
|
defaultCapabilities:
|
|
type: array
|
|
items: { type: string }
|
|
|
|
ShareLink:
|
|
type: object
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
entityType: { type: string }
|
|
entityId: { type: string, format: uuid }
|
|
createdAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time, nullable: true }
|
|
capabilities:
|
|
type: object
|
|
properties:
|
|
read: { type: boolean }
|
|
write: { type: boolean }
|
|
|
|
ShareLinkCreateInput:
|
|
type: object
|
|
required: [entityType, entityId]
|
|
properties:
|
|
entityType: { type: string }
|
|
entityId: { type: string, format: uuid }
|
|
expiresAt: { type: string, format: date-time, nullable: true }
|
|
capabilities:
|
|
type: object
|
|
properties:
|
|
read: { type: boolean }
|
|
write: { type: boolean }
|
|
|
|
ShareLinkCreated:
|
|
type: object
|
|
properties:
|
|
url: { type: string, format: uri }
|
|
expiresAt: { type: string, format: date-time, nullable: true }
|
|
|
|
PushOverdueResult:
|
|
type: object
|
|
properties:
|
|
added: { type: integer }
|
|
|
|
paths:
|
|
/api/v1/calendars:
|
|
get:
|
|
summary: List visible calendars
|
|
tags: [Calendars]
|
|
responses:
|
|
"200":
|
|
description: Calendar list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/Calendar" }
|
|
"401":
|
|
{
|
|
description: Unauthorized,
|
|
content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } },
|
|
}
|
|
post:
|
|
summary: Create calendar
|
|
tags: [Calendars]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CalendarInput" }
|
|
responses:
|
|
"201":
|
|
description: Created calendar
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Calendar" }
|
|
"401": { description: Unauthorized }
|
|
|
|
/api/v1/calendars/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get calendar
|
|
tags: [Calendars]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Calendar" }
|
|
"404": { description: Not found }
|
|
patch:
|
|
summary: Update calendar
|
|
tags: [Calendars]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
name: { type: string }
|
|
visibility: { type: string, enum: [private, household] }
|
|
color: { type: string, nullable: true }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Calendar" }
|
|
delete:
|
|
summary: Delete calendar
|
|
tags: [Calendars]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/events:
|
|
get:
|
|
summary: List events in date range
|
|
tags: [Events]
|
|
parameters:
|
|
- name: from
|
|
in: query
|
|
required: true
|
|
schema: { type: string, format: date-time }
|
|
- name: to
|
|
in: query
|
|
required: true
|
|
schema: { type: string, format: date-time }
|
|
- name: calendarIds
|
|
in: query
|
|
description: "all or comma-separated UUIDs"
|
|
schema: { type: string, default: all }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/CalendarEvent" }
|
|
"400": { description: Missing from/to }
|
|
post:
|
|
summary: Create event
|
|
tags: [Events]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/EventInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CalendarEvent" }
|
|
|
|
/api/v1/events/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get event
|
|
tags: [Events]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CalendarEvent" }
|
|
patch:
|
|
summary: Update event
|
|
tags: [Events]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
allOf:
|
|
- $ref: "#/components/schemas/EventInput"
|
|
description: All fields optional for PATCH
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CalendarEvent" }
|
|
delete:
|
|
summary: Delete event
|
|
tags: [Events]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/lists:
|
|
get:
|
|
summary: List lists
|
|
tags: [Lists]
|
|
parameters:
|
|
- name: type
|
|
in: query
|
|
schema: { type: string }
|
|
description: Filter by list type (e.g. shopping, tasks)
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/ListSummary" }
|
|
post:
|
|
summary: Create list
|
|
tags: [Lists]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ListInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ListSummary" }
|
|
|
|
/api/v1/lists/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get list with items
|
|
tags: [Lists]
|
|
responses:
|
|
"200":
|
|
description: List detail including items array
|
|
patch:
|
|
summary: Update list name or archive
|
|
tags: [Lists]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
name: { type: string }
|
|
archived: { type: boolean }
|
|
responses:
|
|
"200": { description: Updated list }
|
|
delete:
|
|
summary: Archive list
|
|
tags: [Lists]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/lists/{id}/items:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: List items
|
|
tags: [Lists]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/ListItem" }
|
|
post:
|
|
summary: Add item
|
|
tags: [Lists]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ItemInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ListItem" }
|
|
|
|
/api/v1/lists/{id}/items/{itemId}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
- name: itemId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
patch:
|
|
summary: Update or toggle item
|
|
tags: [Lists]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
text: { type: string }
|
|
qty: { type: string, nullable: true }
|
|
notes: { type: string, nullable: true }
|
|
done: { type: boolean }
|
|
dueAt: { type: string, format: date-time, nullable: true }
|
|
responses:
|
|
"200": { description: Updated list with items }
|
|
delete:
|
|
summary: Delete item
|
|
tags: [Lists]
|
|
responses:
|
|
"200": { description: Updated list with items }
|
|
|
|
/api/v1/notes:
|
|
get:
|
|
summary: List notes
|
|
tags: [Notes]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/Note" }
|
|
post:
|
|
summary: Create note
|
|
tags: [Notes]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/NoteInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Note" }
|
|
|
|
/api/v1/notes/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get note
|
|
tags: [Notes]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Note" }
|
|
patch:
|
|
summary: Update note
|
|
tags: [Notes]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
title: { type: string }
|
|
body: { type: string }
|
|
pinned: { type: boolean }
|
|
remindAt: { type: string, format: date-time, nullable: true }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Note" }
|
|
delete:
|
|
summary: Delete note
|
|
tags: [Notes]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/garden/containers:
|
|
get:
|
|
summary: List garden containers
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/GardenContainer" }
|
|
post:
|
|
summary: Create container
|
|
tags: [Garden]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ContainerInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/GardenContainerDetail" }
|
|
|
|
/api/v1/garden/containers/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get container with plants
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/GardenContainerDetail" }
|
|
"404": { description: Not found }
|
|
patch:
|
|
summary: Update container
|
|
tags: [Garden]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
allOf:
|
|
- $ref: "#/components/schemas/ContainerInput"
|
|
description: All fields optional
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/GardenContainerDetail" }
|
|
delete:
|
|
summary: Delete container
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/garden/plants:
|
|
get:
|
|
summary: List plants
|
|
tags: [Garden]
|
|
parameters:
|
|
- name: containerId
|
|
in: query
|
|
schema: { type: string, format: uuid }
|
|
description: Filter by container
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/PlantListItem" }
|
|
post:
|
|
summary: Create plant
|
|
tags: [Garden]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/PlantInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/PlantDetail" }
|
|
|
|
/api/v1/garden/plants/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get plant detail
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/PlantDetail" }
|
|
"404": { description: Not found }
|
|
patch:
|
|
summary: Update plant
|
|
tags: [Garden]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
allOf:
|
|
- $ref: "#/components/schemas/PlantInput"
|
|
description: All fields optional
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/PlantDetail" }
|
|
delete:
|
|
summary: Delete plant
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/garden/plants/{id}/care-logs:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: List care logs for a plant
|
|
tags: [Garden]
|
|
parameters:
|
|
- name: limit
|
|
in: query
|
|
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/CareLog" }
|
|
post:
|
|
summary: Log care performed on a plant
|
|
tags: [Garden]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CareLogInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CareLog" }
|
|
|
|
/api/v1/garden/plants/{id}/care-schedules:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: List care schedules for a plant
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/CareSchedule" }
|
|
post:
|
|
summary: Create or update a care schedule
|
|
tags: [Garden]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CareScheduleInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CareSchedule" }
|
|
|
|
/api/v1/garden/care-schedules/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
patch:
|
|
summary: Enable or disable a care schedule
|
|
tags: [Garden]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CareScheduleToggleInput" }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
delete:
|
|
summary: Delete a care schedule
|
|
tags: [Garden]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/garden/care-schedules/{id}/calendar:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
post:
|
|
summary: Add next due care to calendar
|
|
tags: [Garden]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ScheduleOnCalendarInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/garden/overdue-care/push:
|
|
post:
|
|
summary: Push overdue care items to task list
|
|
tags: [Garden]
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/PushOverdueResult" }
|
|
|
|
/api/v1/share-links:
|
|
get:
|
|
summary: List share links or shareable entity types
|
|
tags: [Sharing]
|
|
parameters:
|
|
- name: entityTypes
|
|
in: query
|
|
schema: { type: string, enum: ["true"] }
|
|
description: When true, returns shareable entity types instead of links
|
|
- name: entityType
|
|
in: query
|
|
schema: { type: string }
|
|
- name: entityId
|
|
in: query
|
|
schema: { type: string, format: uuid }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
oneOf:
|
|
- type: array
|
|
items: { $ref: "#/components/schemas/ShareLink" }
|
|
- type: array
|
|
items: { $ref: "#/components/schemas/ShareableEntityType" }
|
|
post:
|
|
summary: Create a share link
|
|
tags: [Sharing]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ShareLinkCreateInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ShareLinkCreated" }
|
|
|
|
/api/v1/share-links/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
delete:
|
|
summary: Revoke a share link
|
|
tags: [Sharing]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/openapi:
|
|
get:
|
|
summary: OpenAPI specification (YAML)
|
|
tags: [Meta]
|
|
responses:
|
|
"200":
|
|
description: OpenAPI 3.1 YAML document
|
|
content:
|
|
application/yaml:
|
|
schema: { type: string }
|
|
|
|
/api/v1/bangs:
|
|
get:
|
|
summary: Bang stats (total + recent)
|
|
tags: [Bangs]
|
|
parameters:
|
|
- name: limit
|
|
in: query
|
|
schema: { type: integer, default: 10, minimum: 1, maximum: 100 }
|
|
description: Max recent entries to return
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/BangStats" }
|
|
post:
|
|
summary: Record a bang
|
|
tags: [Bangs]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/BangInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Bang" }
|
|
|
|
/api/v1/bangs/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
patch:
|
|
summary: Update bang date
|
|
tags: [Bangs]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/BangUpdateInput" }
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Bang" }
|
|
delete:
|
|
summary: Delete bang
|
|
tags: [Bangs]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
/api/v1/journal/entries:
|
|
get:
|
|
summary: List journal entries for the authenticated user
|
|
tags: [Journal]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/JournalEntry" }
|
|
post:
|
|
summary: Create journal entry
|
|
tags: [Journal]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/JournalEntryInput" }
|
|
responses:
|
|
"201":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/JournalEntry" }
|
|
|
|
/api/v1/journal/entries/{id}:
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
get:
|
|
summary: Get journal entry
|
|
tags: [Journal]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/JournalEntry" }
|
|
patch:
|
|
summary: Update journal entry
|
|
tags: [Journal]
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
allOf:
|
|
- $ref: "#/components/schemas/JournalEntryInput"
|
|
description: All fields optional
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/JournalEntry" }
|
|
delete:
|
|
summary: Delete journal entry
|
|
tags: [Journal]
|
|
responses:
|
|
"200":
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OkResponse" }
|
|
|
|
tags:
|
|
- name: Calendars
|
|
- name: Events
|
|
- name: Lists
|
|
- name: Notes
|
|
- name: Journal
|
|
- name: Garden
|
|
- name: Sharing
|
|
- name: Bangs
|
|
- name: Meta
|