API reference
Every capability of the system, generated from the protobuf sources. Clients call these RPCs through Connect โ there is no other surface. Each section is one domain: its service, then the messages and enums it speaks.
API Key
ApiKeyService
ApiKeyService manages machine credentials (ADR-0016). Every RPC is admin-only via the permission matrix (ADR-0002/0008).
| RPC | Request | Response | Description |
|---|---|---|---|
| CreateApiKey | CreateApiKeyRequest | CreateApiKeyResponse | Mint a new key. The response carries the signed token exactly once. |
| ListApiKeys | ListApiKeysRequest | ListApiKeysResponse | List all keys (records only โ no tokens) |
| RevokeApiKey | ApiKeyRequest | ApiKeyResponse | Revoke a key: dead on the next request. Idempotent. |
ApiKey
ApiKey is an admin-managed machine credential (ADR-0016). The signed token is a JWT whose jti mirrors this record's uid; the record โ not the token โ is the authority: revocation, roles and the act_as allowlist are consulted on every request. The token itself is never stored and never appears on this message; it exists exactly once, as CreateApiKeyResponse.token.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier (also the JWT jti claim) |
| name | string | Human-readable label, e.g. "lukes-claude", "home-assistant" |
| user_uid | string | Personal key: uid of the owning user โ the key acts as that user and inherits (and is capped by) the user's live roles. Empty = standalone: the key is its own principal (e.g. a shared device) with the roles below. |
| roles | repeated Role | Standalone keys only; ignored when user_uid is set. |
| act_as | repeated string | User uids this key may claim via the acting-user mechanism. Empty = no impersonation. Admin-managed; consulted on every honoured claim. |
| created_by | UserRef | Admin who minted the key |
| created_at | google.protobuf.Timestamp | When the key was minted |
| last_used_at | google.protobuf.Timestamp | Best-effort, throttled touch on use โ admin visibility, not an audit |
| revoked_at | google.protobuf.Timestamp | Set = the key is dead on the next request. Never cleared. |
| mutations | repeated Mutation | Audit trail of changes made to this key |
ApiKeyRequest
ApiKeyRequest names one key by uid.
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the key |
ApiKeyResponse
ApiKeyResponse returns one key record.
| Field | Type | Description |
|---|---|---|
| api_key | ApiKey | The key record |
CreateApiKeyRequest
CreateApiKeyRequest mints a new key (ADMIN only).
| Field | Type | Description |
|---|---|---|
| name | string | Human-readable label (required) |
| user_uid | string | Owning user for a personal key; empty mints a standalone key |
| roles | repeated Role | Standalone keys only: the principal's roles. Rejected with user_uid. |
| act_as | repeated string | Acting-user allowlist (uids must exist) |
CreateApiKeyResponse
CreateApiKeyResponse carries the record and โ exactly once โ the signed token. The token is not persisted and cannot be retrieved again.
| Field | Type | Description |
|---|---|---|
| api_key | ApiKey | The stored key record |
| token | string | The signed bearer token; shown only in this response |
ListApiKeysRequest
ListApiKeysRequest lists every key, revoked ones included.
| Field | Type | Description |
|---|---|---|
| limit | int32 | Maximum number of keys to return (0 = no limit) |
ListApiKeysResponse
ListApiKeysResponse returns key records (structurally token-free).
| Field | Type | Description |
|---|---|---|
| api_keys | repeated ApiKey | The key records |
Audit
AuditEntry
AuditEntry is one row of the install-wide audit log: who did what to which entity, when. Entries are written append-only alongside the per-record mutations[] trail, so they survive the record's deletion โ the whole point of a global log. History starts at the deploy that introduced the table; older in-record mutations are not backfilled.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier (UUIDv7 โ time-ordered, so uid order is time order) |
| date | google.protobuf.Timestamp | Timestamp when the change occurred |
| actor | UserRef | User who performed the change. May be empty for unauthenticated creations (first-time setup), matching the mutations[] contract. |
| entity_type | string | Table/kind of the changed entity, e.g. "user", "event", "item" |
| entity_uid | string | UID of the changed entity |
| entity_label | string | Human-readable label of the entity at the time of the change (its name field when it has one), kept here so deleted entities stay identifiable |
| action | MutationAction | What happened: create, update or delete |
| credential_kind | string | How the change was made: "session" or "api_key" (ADR-0016). Empty on rows written before credentials were recorded โ read those as "session". |
| credential_uid | string | ApiKey.uid when credential_kind is "api_key" |
| credential_name | string | ApiKey.name snapshot at write time, so entries survive key deletion |
| subject | UserRef | The credential's own principal, set only when an acting-user claim was honoured and therefore differs from actor. actor is always the effective actor โ who gets chore/reward credit; this answers "via whom". |
Error
ErrorDetail
ErrorDetail rides on Connect errors as a detail message: a stable machine-readable code the web client maps to translated copy, plus optional parameters for interpolation. The error's message string stays English โ it is the server-log form and the client fallback for codes the catalog does not know.
| Field | Type | Description |
|---|---|---|
| code | string | Stable code, e.g. "NOT_ENOUGH_POINTS" |
| params | map<string, string> | Interpolation parameters, e.g. {"entity": "reward"} |
Event
EventService
EventService provides CRUD operations and user management for events
| RPC | Request | Response | Description |
|---|---|---|---|
| Create | CreateEventRequest | EventResponse | Create a new event |
| Read | EventRequest | EventResponse | Read an existing event by UID |
| Update | UpdateEventRequest | EventResponse | Update an existing event |
| Delete | EventRequest | SuccessResponse | Delete an event by UID |
| AddUser | AddUserToEventRequest | EventResponse | Add a user to an event |
| RemoveUser | RemoveUserFromEventRequest | EventResponse | Remove a user from an event |
| ListEvents | ListEventsRequest | ListEventsResponse | List all events |
| ListOccurrences | ListOccurrencesRequest | ListOccurrencesResponse | Everything on the calendar in a window: plain events plus projected occurrences of repeating events |
| SplitOccurrence | SplitOccurrenceRequest | EventResponse | Materialise one occurrence as an editable child row (idempotent) |
| CancelOccurrence | CancelOccurrenceRequest | EventResponse | Cancel one occurrence (tombstone its slot) |
AddUserToEventRequest
AddUserToEventRequest contains the data needed to add a user to an event
| Field | Type | Description |
|---|---|---|
| event_uid | string | UID of the event |
| user_uid | string | UID of the user to add |
CancelOccurrenceRequest
CancelOccurrenceRequest tombstones one occurrence of a repeating event
| Field | Type | Description |
|---|---|---|
| event_uid | string | UID of the repeating parent event |
| occurrence_date | string | The occurrence slot to cancel (the occurrence_date key) |
| time_zone | string | IANA time zone the slot key was produced in (must match the ListOccurrences call that showed it) |
CreateEventRequest
CreateEventRequest contains the data needed to create a new event
| Field | Type | Description |
|---|---|---|
| repeat | EventRepeat | Optional repeat rule for the new event |
| name | string | Name of the new event |
| type | EventType | Type/category of the event |
| start_time | google.protobuf.Timestamp | Event start time |
| end_time | google.protobuf.Timestamp | Event end time |
| description | string | Event description |
| user_uids | repeated string | UIDs of users to associate with the event |
| item_uid | string | Optional UID of related item |
| all_day | bool | The event covers whole days (see Event.all_day) |
| daily_times | bool | The event repeats its clock times on each day of the span (see Event.daily_times) |
| time_zone | string | IANA time zone for span validation arithmetic (e.g. "Europe/Dublin"). Empty falls back to UTC. |
| location | string | Physical or virtual location of the event |
| uid | string | Optional client-minted uid (UUIDv7) so an offline write queue can replay this create safely: a resend lands on ALREADY_EXISTS instead of minting a duplicate (ADR-0019). Empty lets the server mint one. |
Event
Event represents a scheduled event in the family organization system
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the event |
| name | string | Display name of the event |
| type | EventType | Category/type of the event |
| start_time | google.protobuf.Timestamp | When the event begins |
| end_time | google.protobuf.Timestamp | When the event ends |
| description | string | Detailed description of the event |
| mutations | repeated Mutation | Audit trail of changes |
| users | repeated UserRef | Users associated with this event |
| item | ItemRef | Optional related item |
| location | string | Physical or virtual location of the event |
| repeat | EventRepeat | Optional repeat rule: occurrences are derived from this event within a requested window (ListOccurrences) โ no rows are minted per cycle. |
| recurrence_of | string | On an override/tombstone child row: the repeating parent's UID |
| occurrence_date | string | On a child row: the original occurrence slot it replaces (an opaque YYYY-MM-DD key, UTC). Also populated on projected occurrences so the client can name a slot when splitting or cancelling it. |
| cancelled | bool | On a child row: true tombstones the slot โ the occurrence is cancelled |
| all_day | bool | The event covers whole days: start/end are local midnights and the end date is inclusive (start Jul 28, end Jul 30 = three full days) |
| daily_times | bool | The event runs at its start/end clock times on each day of the span separately (a block per day), rather than as one continuous block. start_time holds day one + the daily start clock; end_time holds the last day + the daily end clock. Each day projects as its own occurrence. Note the repeat until cuts span starts only โ a span anchored on or before it runs to completion. |
| span_start_date | string | On projected daily_times occurrences and their child rows: the YYYY-MM-DD start date of the span instance this day belongs to. Never set on stored parent rows. |
| span_end_date | string | The span instance's last covered date (YYYY-MM-DD, inclusive) โ the other half of span_start_date, same rows only. |
EventRepeat
EventRepeat is an event's repeat rule. The anchor is the event's start time: the first occurrence is the event itself, and every shape keeps the anchor's clock and duration.
| Field | Type | Description |
|---|---|---|
| kind | RepeatKind | The shape of the rule (shares the jobs' RepeatKind; adds YEARS) |
| interval | int32 | Every N days/weeks/months/years; minimum 1 |
| weekdays | repeated int32 | Weekdays (0 = Sunday โฆ 6 = Saturday) for the weekly kind โ a set, so Mon/Wed/Fri swimming is one event. The nth-weekday kind reads the first entry. |
| day_of_month | int32 | Day of month (1-31) for MONTHLY_ON_DAY; clamped to the month's length |
| nth | int32 | Which weekday occurrence (1-5, 5 = last) for MONTHLY_ON_NTH_WEEKDAY |
| until | string | Optional inclusive end date (YYYY-MM-DD): no occurrences after it |
EventRequest
EventRequest is used for operations that only need an event UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the event |
EventResponse
EventResponse returns an event object
| Field | Type | Description |
|---|---|---|
| event | Event | The requested event |
ListEventsRequest
ListEventsRequest contains optional filters for listing events
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of events to return (0 means no limit) |
ListEventsResponse
ListEventsResponse returns a list of events
| Field | Type | Description |
|---|---|---|
| events | repeated Event | The list of events |
ListOccurrencesRequest
ListOccurrencesRequest asks for everything on the calendar in a window
| Field | Type | Description |
|---|---|---|
| from | google.protobuf.Timestamp | Window start (inclusive) |
| to | google.protobuf.Timestamp | Window end (exclusive) |
| time_zone | string | IANA time zone for calendar arithmetic (e.g. "Europe/Dublin") โ the zone in which weekdays and month days mean what the family means. Empty falls back to UTC. |
ListOccurrencesResponse
ListOccurrencesResponse returns concrete calendar entries for the window: plain events intersecting it, plus projected occurrences of repeating events (overrides swapped in, cancelled slots dropped)
| Field | Type | Description |
|---|---|---|
| events | repeated Event | The window's events and occurrences |
RemoveUserFromEventRequest
RemoveUserFromEventRequest contains the data needed to remove a user from an event
| Field | Type | Description |
|---|---|---|
| event_uid | string | UID of the event |
| user_uid | string | UID of the user to remove |
SplitOccurrenceRequest
SplitOccurrenceRequest materialises one occurrence of a repeating event as a real child row, so it can be edited on its own
| Field | Type | Description |
|---|---|---|
| event_uid | string | UID of the repeating parent event |
| occurrence_date | string | The occurrence slot to split (the occurrence_date key) |
| time_zone | string | IANA time zone the slot key was produced in (must match the ListOccurrences call that showed it) |
UpdateEventRequest
UpdateEventRequest contains the data needed to update an existing event
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the event to update |
| name | string | Updated name |
| type | EventType | Updated type |
| start_time | google.protobuf.Timestamp | Updated start time |
| end_time | google.protobuf.Timestamp | Updated end time |
| description | string | Updated description |
| user_uids | repeated string | Updated list of associated user UIDs |
| item_uid | string | Updated related item UID |
| all_day | bool | Updated all-day flag (see Event.all_day) |
| daily_times | bool | Updated daily-times flag (see Event.daily_times) |
| time_zone | string | IANA time zone for span validation arithmetic. Empty falls back to UTC. |
| location | string | Updated location |
EventType
EventType defines the different categories of events in the system
| Value | Description |
|---|---|
| EVENT_TYPE_UNSPECIFIED | Default/unknown event type |
| EVENT_TYPE_APPOINTMENT | Scheduled appointments or meetings |
| EVENT_TYPE_BIRTHDAY | Birthday celebrations |
| EVENT_TYPE_DEADLINE | Important deadlines |
| EVENT_TYPE_HOLIDAY | Holidays and special occasions |
Item
ItemService
ItemService provides operations for managing items
| RPC | Request | Response | Description |
|---|---|---|---|
| Create | CreateItemRequest | ItemResponse | Create a new item |
| Read | ItemRequest | ItemResponse | Read an existing item by UID |
| Update | UpdateItemRequest | ItemResponse | Update an existing item |
| Delete | ItemRequest | SuccessResponse | Delete an item by UID |
| AssignUser | AssignUserRequest | ItemResponse | Assign a user to an item |
| SetDeadline | SetDeadlineRequest | ItemResponse | Set a deadline for an item |
| SetRepeat | SetRepeatRequest | ItemResponse | Set or clear an item's repeat rule (standalone jobs only) |
| CompleteItem | CompleteItemRequest | ItemResponse | Mark an item as complete or incomplete |
| SetList | SetListRequest | ItemResponse | Move an item into a list, or make it standalone (empty list_uid) |
| SetParent | SetParentRequest | ItemResponse | Make an item a sub-task of a job, or promote it back to standalone (empty parent_uid). ADR-0025. |
| ListItems | ListItemsRequest | ListItemsResponse | List all items |
ItemListService
ItemListService provides operations for managing item lists
| RPC | Request | Response | Description |
|---|---|---|---|
| Create | CreateItemListRequest | ItemListResponse | Create a new item list |
| Read | ItemListRequest | ItemListResponse | Read an existing item list by UID, including its items |
| Update | UpdateItemListRequest | ItemListResponse | Update an existing item list |
| Delete | ItemListRequest | SuccessResponse | Delete an item list by UID; cascade-deletes the list's items |
| List | ListItemListsRequest | ListItemListsResponse | List all item lists |
| UncheckAll | ItemListRequest | ItemListResponse | Mark every item in the list incomplete; ADMIN/MEMBER only |
AssignUserRequest
AssignUserRequest contains the data needed to assign a user to an item
| Field | Type | Description |
|---|---|---|
| item_uid | string | UID of the item to assign |
| user_uid | string | UID of the user to assign to the item |
CompleteItemRequest
CompleteItemRequest contains the data needed to mark an item complete or incomplete
| Field | Type | Description |
|---|---|---|
| item_uid | string | UID of the item to complete |
| complete | bool | true to mark complete, false to mark incomplete |
CreateItemListRequest
CreateItemListRequest contains the data needed to create a new item list
| Field | Type | Description |
|---|---|---|
| name | string | Name of the new item list |
| uid | string | Optional client-minted uid (UUIDv7) so an offline write queue can replay this create safely: a resend lands on ALREADY_EXISTS instead of minting a duplicate (ADR-0019). Empty lets the server mint one. |
CreateItemRequest
Request messages CreateItemRequest contains the data needed to create a new item
| Field | Type | Description |
|---|---|---|
| title | string | Title of the new item |
| description | string | Description of the new item |
| list_uid | string | UID of the list to add the item to |
| owner_uid | string | Optional owner to assign at creation. A standalone job born with an owner announces "assigned to you"; born without one it announces "up for grabs" โ creating then assigning separately would announce both. |
| uid | string | Optional client-minted uid (UUIDv7) so an offline write queue can replay this create safely: a resend lands on ALREADY_EXISTS instead of minting a duplicate (ADR-0019). Empty lets the server mint one. |
| parent_uid | string | Optional parent job this item is a step of (ADR-0025). Mutually exclusive with list_uid. The new sub-task inherits the parent's owner unless owner_uid is set. |
Item
Item represents a task or todo item in the family organization system
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the item |
| title | string | Title or name of the item |
| description | string | Detailed description of the item |
| complete | bool | Whether the item has been completed |
| owner | UserRef | User who owns or is assigned to this item |
| mutations | repeated Mutation | Audit trail of changes made to this item |
| deadline | string | Optional deadline for completing the item (ISO 8601 format) |
| list | ListRef | Optional list this item belongs to; absent = standalone job |
| repeat | ItemRepeat | Optional repeat rule: completing this item mints its successor (the chained model โ no scheduler, exactly one open instance per chain). Standalone jobs only; list entries never repeat. |
| spawned_next | string | UID of the successor this item minted at close-out. Presence blocks un-completion (the chain has moved on) and records the lineage. |
| parent_uid | string | UID of the job this item is a step of (ADR-0025). Empty = not a sub-task. One level deep: an item with a parent never has children. Sub-tasks carry no repeat rule, list membership or deadline, and never appear on list surfaces outside their parent. |
| sub_items | repeated Item | The item's sub-tasks. Read-time projection populated by ItemService.Read; never stored on the item itself. |
ItemList
ItemList represents a collection of items grouped together
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the item list |
| name | string | Display name of the item list |
| items | repeated Item | Collection of items within this list. Read-time projection populated by ItemListService.Read; never stored on the list itself. |
ItemListRequest
ItemListRequest is used for operations that only need an item list UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the item list |
ItemListResponse
ItemListResponse returns an item list object
| Field | Type | Description |
|---|---|---|
| item_list | ItemList | The requested item list |
ItemRepeat
ItemRepeat is a job's repeat rule. The successor's deadline lands on the next occurrence strictly after max(old deadline, completion time) โ early completion keeps the cadence, late completion skips the missed slots.
| Field | Type | Description |
|---|---|---|
| kind | RepeatKind | The shape of the rule; decides which of the fields below apply |
| interval | int32 | Every N days/weeks/months; minimum 1 |
| weekday | int32 | Weekday for the weekly and nth-weekday kinds: 0 = Sunday โฆ 6 = Saturday |
| day_of_month | int32 | Day of month (1-31) for MONTHLY_ON_DAY; clamped to the month's length |
| nth | int32 | Which weekday occurrence (1-5) for MONTHLY_ON_NTH_WEEKDAY; 5 means the last such weekday of the month |
| time_zone | string | IANA time zone the rule lives in (e.g. "Europe/Dublin"), captured when the rule is set. Successor math runs in this zone so the chain keeps a stable wall clock across DST boundaries. Empty falls back to the deadline's own offset. |
ItemRequest
Common request/response patterns ItemRequest is used for operations that only need an item UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the item |
ItemResponse
ItemResponse returns an item object
| Field | Type | Description |
|---|---|---|
| item | Item | The requested item |
ListItemListsRequest
ListItemListsRequest contains optional filters for listing item lists
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of item lists to return (0 means no limit) |
ListItemListsResponse
ListItemListsResponse returns a list of item lists
| Field | Type | Description |
|---|---|---|
| item_lists | repeated ItemList | The list of item lists |
ListItemsRequest
ListItemsRequest contains optional filters for listing items
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of items to return (0 means no limit) |
| include_sub_tasks | bool | Include sub-tasks in the result. Off by default so list surfaces never show a step outside its parent (ADR-0025); the web and Android caches opt in because they hold the full collection and filter locally. |
ListItemsResponse
ListItemsResponse returns a list of items
| Field | Type | Description |
|---|---|---|
| items | repeated Item | The list of items |
SetDeadlineRequest
SetDeadlineRequest contains the data needed to set a deadline for an item
| Field | Type | Description |
|---|---|---|
| item_uid | string | UID of the item to set deadline for |
| deadline | string | Deadline to set (ISO 8601 format) |
SetListRequest
SetListRequest contains the data needed to move an item into or out of a list
| Field | Type | Description |
|---|---|---|
| item_uid | string | UID of the item to move |
| list_uid | string | UID of the list to place the item in; empty = make the item standalone |
SetParentRequest
SetParentRequest makes an item a sub-task of a job, or promotes it back to a standalone job (ADR-0025)
| Field | Type | Description |
|---|---|---|
| item_uid | string | UID of the item to convert |
| parent_uid | string | UID of the parent job; empty = promote to a standalone job |
SetRepeatRequest
SetRepeatRequest sets or clears an item's repeat rule
| Field | Type | Description |
|---|---|---|
| item_uid | string | UID of the item |
| repeat | ItemRepeat | The rule to set; absent clears the repeat (the chain ends here) |
UpdateItemListRequest
UpdateItemListRequest contains the data needed to update an existing item list
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the item list to update |
| name | string | Updated name |
UpdateItemRequest
UpdateItemRequest contains the data needed to update an existing item
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the item to update |
| title | string | Updated title |
| description | string | Updated description |
| complete | bool | Updated completion status |
| deadline | string | Updated deadline (ISO 8601 format) |
RepeatKind
RepeatKind is the shape of a repeat rule.
| Value | Description |
|---|---|
| REPEAT_KIND_UNSPECIFIED | No rule โ the item does not repeat |
| REPEAT_KIND_EVERY_N_DAYS | Every interval days |
| REPEAT_KIND_EVERY_N_WEEKS_ON_WEEKDAY | Every interval weeks on weekday |
| REPEAT_KIND_MONTHLY_ON_DAY | Every interval months on day_of_month (clamped to month length) |
| REPEAT_KIND_MONTHLY_ON_NTH_WEEKDAY | Every interval months on the nth weekday (e.g. 2nd Thursday) |
| REPEAT_KIND_EVERY_N_YEARS | Every interval years on the anchor date (events only; job rules reject it) |
Meal
MealService
MealService provides operations for managing the meal library
| RPC | Request | Response | Description |
|---|---|---|---|
| Create | CreateMealRequest | MealResponse | Create a new meal |
| Read | MealRequest | MealResponse | Read an existing meal by UID |
| Update | UpdateMealRequest | MealResponse | Update an existing meal |
| Delete | MealRequest | SuccessResponse | Delete a meal by UID. Rota slots and future overrides referencing it are cleared; frozen week snapshots keep their stamped name copy so history stays readable. |
| List | ListMealsRequest | ListMealsResponse | List all meals |
| PushIngredients | PushIngredientsRequest | PushIngredientsResponse | Add the meal's ingredient lines to an item list, one item per line, skipping lines already open on the list |
CreateMealRequest
CreateMealRequest contains the data needed to create a new meal
| Field | Type | Description |
|---|---|---|
| name | string | Name of the new meal |
| ingredients | repeated string | Ingredient lines, one per entry |
| notes | string | Free-form notes |
| uid | string | Optional client-minted uid (UUIDv7) so an offline write queue can replay this create safely: a resend lands on ALREADY_EXISTS instead of minting a duplicate (ADR-0019). Empty lets the server mint one. |
ListMealsRequest
ListMealsRequest contains optional filters for listing meals
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of meals to return (0 means no limit) |
ListMealsResponse
ListMealsResponse returns a list of meals
| Field | Type | Description |
|---|---|---|
| meals | repeated Meal | The list of meals |
Meal
Meal is a reusable dish in the family's meal library. Putting a meal on a day is a rota slot or a day override carrying a MealRef โ the library row holds what the dish is, the rota holds when it happens and who cooks.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the meal |
| name | string | Display name of the meal ("Chicken curry") |
| ingredients | repeated string | Ingredient lines, one per entry, quantity in free text ("500g mince"). Each line becomes one shopping-list item when pushed. |
| notes | string | Free-form notes (method pointers, cookbook page, allergy flags) |
| mutations | repeated Mutation | Audit trail of changes made to this meal |
MealRequest
MealRequest is used for operations that only need a meal UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the meal |
MealResponse
MealResponse returns a meal object
| Field | Type | Description |
|---|---|---|
| meal | Meal | The requested meal |
PushIngredientsRequest
PushIngredientsRequest asks for a meal's ingredient lines to be added to an item list as one item per line
| Field | Type | Description |
|---|---|---|
| meal_uid | string | UID of the meal whose ingredients to push |
| list_uid | string | UID of the target item list |
PushIngredientsResponse
PushIngredientsResponse reports what the push did. A line is skipped when an incomplete item with the same title (case-insensitive) already sits on the target list; completed items do not block re-adding.
| Field | Type | Description |
|---|---|---|
| added | int32 | Number of items created on the list |
| skipped | int32 | Number of lines skipped as already on the list |
UpdateMealRequest
UpdateMealRequest contains the data needed to update an existing meal. Renaming a meal does not rewrite the name copies stamped on rota slots, overrides or week snapshots โ those are deliberate history (ListRef precedent).
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the meal to update |
| name | string | Updated name |
| ingredients | repeated string | Updated ingredient lines (full replacement) |
| notes | string | Updated notes |
Meal Rota
MealRotaService
MealRotaService runs the standing rota: the board itself, per-date overrides, and the merged read the calendar and Today consume. Reading a range that covers completed weeks freezes them on first touch (lazy snapshots), so the read may write.
| RPC | Request | Response | Description |
|---|---|---|---|
| Get | GetMealRotaRequest | MealRotaResponse | Get the raw rota for editing |
| SetSlot | SetMealRotaSlotRequest | MealRotaResponse | Replace one weekday slot; assigning a new cook notifies them |
| SetTimeZone | SetMealRotaTimeZoneRequest | MealRotaResponse | Change the rota's time zone |
| SetOverride | SetMealOverrideRequest | MealOverrideResponse | Pin one date (swap, one-off or skip); assigning a new cook notifies them |
| ClearOverride | ClearMealOverrideRequest | SuccessResponse | Unpin a date, resuming the rota |
| ListResolvedDays | ListResolvedMealDaysRequest | ListResolvedMealDaysResponse | The merged day-by-day view: override beats rota, completed weeks read from their snapshot (frozen on first touch) |
ClearMealOverrideRequest
ClearMealOverrideRequest unpins a date, putting it back on the rota. Past dates are rejected.
| Field | Type | Description |
|---|---|---|
| date | string | The date to unpin, YYYY-MM-DD |
GetMealRotaRequest
GetMealRotaRequest asks for the raw rota (the edit view).
ListResolvedMealDaysRequest
ListResolvedMealDaysRequest asks for the merged day-by-day view of an inclusive date range (at most ~100 days).
| Field | Type | Description |
|---|---|---|
| from | string | First date, YYYY-MM-DD |
| to | string | Last date, YYYY-MM-DD |
ListResolvedMealDaysResponse
ListResolvedMealDaysResponse returns one entry per date in the range. Empty days are included with source EMPTY so callers can render gaps.
| Field | Type | Description |
|---|---|---|
| days | repeated ResolvedMealDay | The resolved days, in date order |
| time_zone | string | The rota's time zone, for callers reasoning about "today" |
MealDayOverride
MealDayOverride pins one specific date. The row is a full replacement: while it exists the day is exactly what it says, and rota edits no longer reach that date. An override with neither meal nor cook is a skipped day; deleting the row puts the date back on the rota.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the override |
| date | string | The pinned date, YYYY-MM-DD in the rota's time zone |
| meal | MealRef | The dish for this date; name stamped from the library at set time |
| cook | UserRef | The single cook on duty; may be absent |
| mutations | repeated Mutation | Audit trail of changes made to this override |
MealOverrideResponse
MealOverrideResponse returns an override object
| Field | Type | Description |
|---|---|---|
| override | MealDayOverride | The pinned day |
MealRota
MealRota is the family's standing weekly menu board โ a singleton row (uid "rota", the system-settings precedent). Editing a slot changes every future week at once; a specific date is pinned with a MealDayOverride.
| Field | Type | Description |
|---|---|---|
| uid | string | Always "rota" |
| slots | repeated MealRotaSlot | At most seven entries, keyed by weekday |
| time_zone | string | IANA time zone the rota's days are reckoned in ("Europe/Dublin"). Empty is treated as UTC; the web board offers the browser's zone. |
| reminder_last_sent | string | Local date (YYYY-MM-DD in time_zone) the day-of cooking reminder last fired. Written with a locked compare-and-set and no mutation entry (the api-key last-used precedent) โ bookkeeping, not history. |
| mutations | repeated Mutation | Audit trail of changes made to the rota |
MealRotaResponse
MealRotaResponse returns the rota object
| Field | Type | Description |
|---|---|---|
| rota | MealRota | The standing rota |
MealRotaSlot
MealRotaSlot is one weekday of the standing rota. The slots list is sparse: an absent weekday is an empty day.
| Field | Type | Description |
|---|---|---|
| weekday | int32 | ISO weekday, 1 = Monday .. 7 = Sunday |
| meal | MealRef | The dish for this weekday; name stamped from the library at set time |
| cook | UserRef | The single cook on duty; may be absent (nobody assigned) |
MealWeek
MealWeek is the frozen record of a completed week. Its uid is the week's Monday (natural key โ materialization is write-if-absent and idempotent). A row is never rewritten once present.
| Field | Type | Description |
|---|---|---|
| uid | string | Always equal to week_start |
| week_start | string | The week's Monday, YYYY-MM-DD |
| days | repeated MealWeekDay | Exactly seven entries, Monday through Sunday |
| time_zone | string | The rota's time zone at freeze time |
| mutations | repeated Mutation | Single CREATE entry recording who caused the freeze |
MealWeekDay
MealWeekDay is one day inside a frozen week. Meal and cook are stamped copies โ the meal name survives later meal deletion; the uid may dangle.
| Field | Type | Description |
|---|---|---|
| date | string | The date, YYYY-MM-DD |
| meal | MealRef | The dish that stood on this date when the week froze |
| cook | UserRef | The cook that stood on this date when the week froze |
| source | MealDaySource | How the day resolved at freeze time (ROTA, OVERRIDE or EMPTY) |
ResolvedMealDay
ResolvedMealDay is the merged answer for one date: override beats rota beats empty, and completed weeks come from their snapshot.
| Field | Type | Description |
|---|---|---|
| date | string | The date, YYYY-MM-DD |
| meal | MealRef | The dish on this date, if any |
| cook | UserRef | The cook on duty, if any |
| source | MealDaySource | Where the answer came from |
SetMealOverrideRequest
SetMealOverrideRequest pins one date. Upserts by date; dates already in the past (in the rota's zone) are rejected โ finished days belong to their snapshot. Both uids empty pins a skip.
| Field | Type | Description |
|---|---|---|
| date | string | The date to pin, YYYY-MM-DD |
| meal_uid | string | UID of the meal for this date, or empty for none |
| cook_uid | string | UID of the cook for this date, or empty for none |
SetMealRotaSlotRequest
SetMealRotaSlotRequest replaces one weekday slot. Empty meal_uid clears the dish; empty cook_uid clears the cook; both empty clears the slot.
| Field | Type | Description |
|---|---|---|
| weekday | int32 | ISO weekday to set, 1 = Monday .. 7 = Sunday |
| meal_uid | string | UID of the meal to put on this weekday, or empty to clear |
| cook_uid | string | UID of the cook on duty, or empty to clear |
SetMealRotaTimeZoneRequest
SetMealRotaTimeZoneRequest changes the zone the rota's days are reckoned in.
| Field | Type | Description |
|---|---|---|
| time_zone | string | IANA time zone name ("Europe/Dublin") |
MealDaySource
MealDaySource says where a resolved day's content came from.
| Value | Description |
|---|---|
| MEAL_DAY_SOURCE_UNSPECIFIED | Unknown source |
| MEAL_DAY_SOURCE_EMPTY | Nothing planned |
| MEAL_DAY_SOURCE_ROTA | The standing rota slot for that weekday |
| MEAL_DAY_SOURCE_OVERRIDE | A pinned one-day override (swap, one-off or skip) |
| MEAL_DAY_SOURCE_SNAPSHOT | Read from a frozen past-week snapshot |
Mutation
Mutation
Mutation represents an audit record of a change made to an entity
| Field | Type | Description |
|---|---|---|
| date | google.protobuf.Timestamp | Timestamp when the mutation occurred |
| user | UserRef | User who performed the mutation |
| action | MutationAction | Type of action that was performed |
MutationAction
MutationAction defines the types of actions that can be performed on entities
| Value | Description |
|---|---|
| MUTATION_ACTION_UNSPECIFIED | Default/unknown action type |
| MUTATION_ACTION_CREATE | Entity was created |
| MUTATION_ACTION_UPDATE | Entity was updated/modified |
| MUTATION_ACTION_DELETE | Entity was deleted |
| MUTATION_ACTION_EXPORT | Data was exported (read-only; records that an archive left the system) |
| MUTATION_ACTION_IMPORT | Data was imported/restored from a backup archive |
Notification
NotificationService
NotificationService serves each member their own inbox โ every RPC is scoped to the requester; there is no way to read another member's rows.
| RPC | Request | Response | Description |
|---|---|---|---|
| List | ListNotificationsRequest | ListNotificationsResponse | List the requester's notifications, newest first |
| MarkRead | NotificationRequest | NotificationResponse | Mark one of the requester's notifications read |
| MarkAllRead | MarkAllReadRequest | SuccessResponse | Mark all of the requester's notifications read |
| Subscribe | SubscribeRequest | Notification | Stream the requester's notifications as they are minted, until the client disconnects. Live delivery only โ missed rows are in List. |
| Watch | WatchRequest | DataChange | Stream data-change announcements: an entity name per committed write, so clients can silently re-fetch what they're showing. No payload โ the announcement is a doorbell, the entity's List RPC is the ledger. |
DataChange
DataChange announces that rows of an entity changed
| Field | Type | Description |
|---|---|---|
| entity | string | Lowercase proto message name: "item", "reward", "event", "user", "itemlist", "notification" |
ListNotificationsRequest
ListNotificationsRequest asks for the requester's inbox
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of rows to return (0 = server default) |
ListNotificationsResponse
ListNotificationsResponse returns the requester's rows, newest first
| Field | Type | Description |
|---|---|---|
| notifications | repeated Notification | The notifications |
MarkAllReadRequest
MarkAllReadRequest marks the requester's whole inbox read
Notification
Notification is one row of a member's inbox. Rows are minted server-side at the emission points (fan-out on write), pruned to the newest ~100 per recipient, and carry a pre-rendered message plus a link into the app.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the notification |
| recipient | UserRef | Who this row belongs to โ the inbox owner |
| actor | UserRef | Who did the thing (their mark leads the row in the feed) |
| kind | NotificationKind | What kind of moment this was |
| message | string | The rendered sentence, second person where the recipient is the target |
| link | string | App path the row opens (e.g. "/jobs?view=everyone") |
| created_at | google.protobuf.Timestamp | When the moment happened |
| read | bool | Whether the recipient has read this row |
NotificationRequest
NotificationRequest names one notification by UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the notification |
NotificationResponse
NotificationResponse returns one notification
| Field | Type | Description |
|---|---|---|
| notification | Notification | The notification |
SubscribeRequest
SubscribeRequest opens the requester's live stream
WatchRequest
WatchRequest opens the live data-change stream
NotificationKind
NotificationKind names the moment a notification was minted for.
| Value | Description |
|---|---|
| NOTIFICATION_KIND_UNSPECIFIED | Unknown kind |
| NOTIFICATION_KIND_JOB_COMPLETED | Someone completed a job |
| NOTIFICATION_KIND_JOB_UP_FOR_GRABS | A job (or bounty) landed in the up-for-grabs pool |
| NOTIFICATION_KIND_REWARD_GRANTED | A reward was granted to the recipient |
| NOTIFICATION_KIND_JOB_ASSIGNED | A job was assigned to the recipient |
| NOTIFICATION_KIND_REWARD_CLAIMED | Someone claimed a reward (hand-claims only โ points banked by finishing a job are part of that completion's announcement) |
| NOTIFICATION_KIND_MEAL_ASSIGNED | The recipient was put on cooking duty for a planned meal |
| NOTIFICATION_KIND_MEAL_REMINDER | Day-of nudge: the recipient cooks tonight |
Push
PushService
PushService manages Web Push device registrations. Every RPC is scoped to the requester โ there is no way to see or touch another member's devices.
| RPC | Request | Response | Description |
|---|---|---|---|
| GetVapidPublicKey | GetVapidPublicKeyRequest | GetVapidPublicKeyResponse | The public VAPID key browsers subscribe with |
| Subscribe | PushSubscribeRequest | PushSubscriptionResponse | Register this device. Re-subscribing an endpoint that already has a row replaces it โ re-owned by the requester (shared-device account switch). |
| ListSubscriptions | ListPushSubscriptionsRequest | ListPushSubscriptionsResponse | List the requester's subscribed devices |
| Unsubscribe | PushSubscriptionRequest | SuccessResponse | Remove one of the requester's subscriptions |
| SendTest | SendTestPushRequest | SuccessResponse | Push a localized test message to the requester's devices |
GetVapidPublicKeyRequest
GetVapidPublicKeyRequest asks for the install's public VAPID key.
GetVapidPublicKeyResponse
GetVapidPublicKeyResponse carries the base64url public VAPID key the browser needs for pushManager.subscribe.
| Field | Type | Description |
|---|---|---|
| public_key | string | The public VAPID key (base64url) |
ListPushSubscriptionsRequest
ListPushSubscriptionsRequest lists the requester's own subscriptions.
ListPushSubscriptionsResponse
ListPushSubscriptionsResponse returns the requester's device rows.
| Field | Type | Description |
|---|---|---|
| subscriptions | repeated PushSubscription | The subscriptions |
PushSubscribeRequest
PushSubscribeRequest registers (or re-registers) the calling device.
| Field | Type | Description |
|---|---|---|
| endpoint | string | Push-service URL from the browser's PushSubscription (required) |
| p256dh | string | Client public key, base64url (required) |
| auth | string | Auth secret, base64url (required) |
| label | string | Friendly device label derived client-side from the user agent |
PushSubscription
PushSubscription is one browser's Web Push registration โ one row per device the member switched notifications on for. The p256dh/auth crypto material is server-internal (structurally scrubbed from responses); the endpoint survives responses so a device can recognise its own row.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the subscription |
| user_uid | string | Owning member โ pushes for this member fan out to their rows |
| endpoint | string | Push-service URL the browser minted (unique per registration) |
| p256dh | string | Client public key for payload encryption (server-internal) |
| auth | string | Auth secret for payload encryption (server-internal) |
| label | string | Human-readable device label, e.g. "Chrome on Android" |
| created_at | google.protobuf.Timestamp | When the device subscribed |
| mutations | repeated Mutation | Audit trail of changes made to this subscription |
PushSubscriptionRequest
PushSubscriptionRequest names one subscription by uid.
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the subscription |
PushSubscriptionResponse
PushSubscriptionResponse returns one subscription row.
| Field | Type | Description |
|---|---|---|
| subscription | PushSubscription | The subscription |
SendTestPushRequest
SendTestPushRequest pushes a test notification to every one of the requester's subscribed devices, ignoring per-kind preferences.
Refs
ItemRef
ItemRef represents a reference to an item
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier of the referenced item |
ListRef
ListRef represents a reference to an item list
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier of the referenced item list |
| name | string | Display name of the referenced item list |
MealRef
MealRef represents a reference to a meal. The name is stamped from the meal row when the reference is written and is never rewritten โ if the meal is later deleted, the copy keeps history readable.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier of the referenced meal |
| name | string | Display name of the referenced meal at the time the reference was made |
SuccessResponse
SuccessResponse indicates whether an operation was successful
| Field | Type | Description |
|---|---|---|
| success | bool | True if the operation succeeded |
UserRef
UserRef represents a reference to a user with relationship information
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier of the referenced user |
| name | string | Display name of the referenced user |
| type | RelationType | Type of relationship to the referenced user |
RelationType
RelationType defines the types of relationships between users.
Stored user relations carry only PARENT, PARTNER and EX_PARTNER (plus OWNER on non-user entities such as events, items and rewards). Everything else is derived server-side and appears only in User.derived_relations (ADR-0004).
| Value | Description |
|---|---|
| RELATION_TYPE_UNSPECIFIED | Default/unknown relationship type |
| RELATION_TYPE_PARENT | Parent edge, stored on the child: the referenced user is a parent of the holder. Any number of parent edges per person (ADR-0004). |
| RELATION_TYPE_CHILD | Child relationship. No longer stored: writes are auto-inverted into a PARENT edge on the referenced user, and CHILD appears in derived_relations as the inverse of a parent edge (ADR-0004). |
| RELATION_TYPE_OWNER | Owner relationship (user owns or is responsible for something) |
| RELATION_TYPE_PARTNER | Live partnership, symmetric, stored once (on the member the write targeted). At most one live partnership per person. |
| RELATION_TYPE_EX_PARTNER | Ended partnership. Inert: never walked by derivations, but still blocks member deletion like any stored edge. |
| RELATION_TYPE_SIBLING | Derived only: shares at least one parent. |
| RELATION_TYPE_GRANDPARENT | Derived only: parent of a parent. |
| RELATION_TYPE_GRANDCHILD | Derived only: child of a child. |
| RELATION_TYPE_AUNT_UNCLE | Derived only: a parent's sibling. |
| RELATION_TYPE_COUSIN | Derived only: a parent's sibling's child. |
| RELATION_TYPE_PARENT_IN_LAW | Derived only: the partner's parent. |
| RELATION_TYPE_CHILD_IN_LAW | Derived only: a child's partner. |
Reward
RewardService
RewardService provides operations for managing rewards
| RPC | Request | Response | Description |
|---|---|---|---|
| Create | CreateRewardRequest | RewardResponse | Create a new reward |
| Read | RewardRequest | RewardResponse | Read an existing reward by UID |
| Update | UpdateRewardRequest | RewardResponse | Update an existing reward |
| Delete | RewardRequest | SuccessResponse | Delete a reward by UID |
| Claim | ClaimRewardRequest | RewardResponse | Claim a reward |
| LinkItem | LinkRewardItemRequest | RewardResponse | Link a reward to an item, or unlink it (empty item_uid). Only unclaimed points rewards can be linked, and never to an item in a list (ADR-0015). |
| GetUserRewards | GetUserRewardsRequest | RewardListResponse | Get all rewards for a specific user |
| ListRewards | ListRewardsRequest | ListRewardsResponse | List all rewards |
ClaimRewardRequest
ClaimRewardRequest contains the data needed to claim a reward
| Field | Type | Description |
|---|---|---|
| reward_uid | string | UID of the reward to claim |
| user_uid | string | UID of the user claiming the reward |
CreateRewardRequest
CreateRewardRequest contains the data needed to create a new reward
| Field | Type | Description |
|---|---|---|
| name | string | Name of the reward |
| description | string | Description of the reward |
| type | RewardType | Type of reward |
| value | int32 | Value of the reward |
| user_uid | string | UID of the user to reward |
| item_uid | string | UID of the item/task that triggered the reward (optional) |
| cost | int32 | Points cost to claim the reward (0 = free). Rejected for REWARD_TYPE_POINTS โ points are earnings, not purchases (ADR-0003). |
| uid | string | Optional client-minted uid (UUIDv7) so an offline write queue can replay this create safely: a resend lands on ALREADY_EXISTS instead of minting a duplicate (ADR-0019). Empty lets the server mint one. |
GetUserRewardsRequest
GetUserRewardsRequest contains the data needed to get all rewards for a user
| Field | Type | Description |
|---|---|---|
| user_uid | string | UID of the user to get rewards for |
| unclaimed_only | bool | Whether to include only unclaimed rewards |
LinkRewardItemRequest
LinkRewardItemRequest contains the data needed to link a reward to an item, or to unlink it (ADR-0015). Linking a points reward to a job makes the job worth those points: completing the job auto-claims the reward.
| Field | Type | Description |
|---|---|---|
| reward_uid | string | UID of the reward to link or unlink |
| item_uid | string | UID of the item to link the reward to; empty = unlink the reward |
ListRewardsRequest
ListRewardsRequest contains optional filters for listing rewards
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of rewards to return (0 means no limit) |
ListRewardsResponse
ListRewardsResponse returns a list of rewards
| Field | Type | Description |
|---|---|---|
| rewards | repeated Reward | The list of rewards |
Reward
Reward represents a reward given to users for completing tasks or achievements
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the reward |
| name | string | Name or title of the reward |
| description | string | Detailed description of what the reward is for |
| type | RewardType | Type of reward (points, badge, privilege, etc.) |
| value | int32 | Numerical value of the reward (points, currency amount, etc.) |
| user | UserRef | User who earned this reward |
| item | ItemRef | Item or task that triggered this reward (optional) |
| earned_at | google.protobuf.Timestamp | When the reward was earned |
| claimed_at | google.protobuf.Timestamp | When the reward was claimed/redeemed (optional) |
| claimed | bool | Whether the reward has been claimed |
| mutations | repeated Mutation | Audit trail of changes made to this reward |
| cost | int32 | Points cost to claim this reward (0 = free). A costed reward is paid from the owner's points ledger when claimed (ADR-0003). |
| spent_on | string | UID of the claimed reward this SPEND entry paid for. Only set on REWARD_TYPE_SPEND entries, which are written by Claim (ADR-0003). |
RewardListResponse
RewardListResponse returns a list of rewards
| Field | Type | Description |
|---|---|---|
| rewards | repeated Reward | List of rewards |
RewardRequest
RewardRequest is used for operations that only need a reward UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the reward |
RewardResponse
RewardResponse returns a reward object
| Field | Type | Description |
|---|---|---|
| reward | Reward | The requested reward |
UpdateRewardRequest
UpdateRewardRequest contains the data needed to update an existing reward
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the reward to update |
| name | string | Updated name |
| description | string | Updated description |
| type | RewardType | Updated type |
| value | int32 | Updated value |
RewardType
RewardType defines the different types of rewards that can be given
| Value | Description |
|---|---|
| REWARD_TYPE_UNSPECIFIED | Default/unknown reward type |
| REWARD_TYPE_POINTS | Points-based reward |
| REWARD_TYPE_BADGE | Achievement badge |
| REWARD_TYPE_PRIVILEGE | Special privilege or permission |
| REWARD_TYPE_CURRENCY | Monetary reward |
| REWARD_TYPE_ITEM | Physical item or gift |
| REWARD_TYPE_SPEND | Points ledger debit written by Claim when a costed reward is claimed (ADR-0003). Never created directly; balance = sum POINTS - sum SPEND. |
Role
Permission
Permission defines which roles can access a specific RPC method.
This message is used for documentation and client-side reference only. Actual authorization enforcement happens server-side in the auth interceptor. Permissions cannot be modified programmatically - any changes require a code update and new version release.
| Field | Type | Description |
|---|---|---|
| service | string | Service name (e.g., "UserService", "EventService") |
| method | string | Method name (e.g., "Create", "Read", "Update", "Delete") |
| allowed_roles | repeated Role | Roles that are allowed to call this method |
PermissionMatrix
PermissionMatrix provides a read-only reference to all service permissions.
This allows clients to understand what each role can do, enabling:
- Client-side validation before making requests (UX optimization)
- Permission documentation and discovery
- CLI help text generation
Note: This is for reference/documentation only. The server is authoritative and always enforces permissions regardless of client-side checks.
| Field | Type | Description |
|---|---|---|
| permissions | repeated Permission | All permission rules for the system |
Role
Role defines user roles in the family organization system.
Roles determine what actions a user can perform in the system. The permission model is enforced server-side via the authorization interceptor.
| Value | Description |
|---|---|
| ROLE_UNSPECIFIED | Unspecified/unknown role (should not be used in practice). This is the zero value required by Protocol Buffers. |
| ROLE_ADMIN | Administrator with full system access. Can manage all users, events, items, and rewards. Typical use: Parents or guardians |
| ROLE_MEMBER | Standard family member with moderate permissions. Can manage their own resources and participate in family activities. Typical use: Adult family members, teenagers |
| ROLE_CHILD | Child user with limited permissions. Can view content and complete assigned tasks. Typical use: Young children |
| ROLE_GUEST | Guest user with read-only access. Can only view content they're explicitly granted access to. Typical use: Extended family, friends, babysitters |
Settings
SystemSettings
SystemSettings is the storage shape of the install-wide settings singleton (one row, uid "system"). It is never embedded in an RPC response โ the SystemService settings RPCs carry scalar fields instead, precisely so jwt_secret can never cross the wire.
| Field | Type | Description |
|---|---|---|
| uid | string | Fixed row identifier, always "system" |
| family_name | string | Household display name |
| token_expiry_seconds | int64 | Lifetime of newly issued login tokens, in seconds. Changing it affects new sign-ins only; outstanding tokens keep their embedded expiry. |
| jwt_secret | string | HS256 signing secret for login tokens. Server-internal: seeded on first boot (from EAG_JWT_SECRET if set, otherwise generated), replaced by RotateJWTSecret, and structurally scrubbed from responses like password_hash. |
| mutations | repeated Mutation | Audit trail of changes made to the settings |
| default_locale | string | Family-wide default locale (BCP-47 tag), applied to members whose own settings carry no locale. Empty means English. |
| vapid_public_key | string | Public half of the Web Push VAPID keypair, generated on first boot and handed to browsers via PushService.GetVapidPublicKey. |
| vapid_private_key | string | Private half of the VAPID keypair. Server-internal: structurally scrubbed from responses like jwt_secret. |
| mcp_enabled | bool | Whether the /mcp endpoint answers at all. Absent means false: MCP is off until an admin turns it on. |
System
SystemService
SystemService reports install-level state.
It is the home for install-level concerns: setup state, the install-wide settings singleton and the global audit log. It deliberately ships with only what is needed now โ no speculative endpoints.
| RPC | Request | Response | Description |
|---|---|---|---|
| SetupStatus | SetupStatusRequest | SetupStatusResponse | SetupStatus reports whether first-time setup is needed. This is a public endpoint: the web app calls it unauthenticated to decide whether to show the welcome/first-admin flow instead of sign-in. |
| GetSystemSettings | GetSystemSettingsRequest | GetSystemSettingsResponse | GetSystemSettings returns the install-wide settings. Readable by every signed-in member: the family name is shared identity and the token lifetime is not sensitive. The JWT secret is never included. |
| UpdateSystemSettings | UpdateSystemSettingsRequest | GetSystemSettingsResponse | UpdateSystemSettings changes the install-wide settings (ADMIN only). Field presence decides what changes; absent fields are left untouched. |
| RotateJWTSecret | RotateJWTSecretRequest | SuccessResponse | RotateJWTSecret replaces the token signing secret with a freshly generated one (ADMIN only). Every outstanding token becomes invalid immediately โ including the caller's. The secret itself never crosses the wire, in either direction. |
| AuditLog | AuditLogRequest | AuditLogResponse | AuditLog pages backward through the install-wide audit trail (ADMIN only), newest first, optionally filtered by actor and entity type. |
| CheckForUpdate | CheckForUpdateRequest | CheckForUpdateResponse | CheckForUpdate asks the release feed for the newest published version and compares it with the running one (ADMIN only). On demand only โ the server never phones home unprompted. |
| ExportData | ExportDataRequest | ExportDataResponse | ExportData builds a downloadable archive of the install's data (ADMIN only, on demand). The archive is a gzipped JSON envelope carried as opaque bytes โ deliberately outside the sanitize walk so BACKUP mode can include credential material (ADR-0022). |
| ImportData | ImportDataRequest | ImportDataResponse | ImportData restores a backup archive produced by ExportData (ADR-0023). On an empty install this is a public setup window like UserService/Create (ADR-0018), honouring X-Initial-Admin-Token when configured. Once any user exists it requires an ADMIN and an explicit wipe flag; the response then carries a backup of the replaced data. |
| ExportUserData | ExportUserDataRequest | ExportDataResponse | ExportUserData builds a downloadable archive of one member's data โ their own record plus what they own or take part in, always scrubbed (ADR-0027). Every signed-in member may export themselves; exporting another member requires ADMIN. The archive reuses ADR-0022's opaque bytes envelope and can never be restored by ImportData. |
AuditLogRequest
AuditLogRequest describes one page of the audit log.
| Field | Type | Description |
|---|---|---|
| page_size | int32 | Maximum entries to return. Server clamps: default 50, cap 200. |
| cursor | string | UID of the last entry from the previous page; empty for the first page. Entry uids are time-ordered (UUIDv7), so paging is uid < cursor. |
| actor_uid | string | Restrict to changes performed by this user |
| entity_type | string | Restrict to changes on this entity type, e.g. "user", "event" |
AuditLogResponse
AuditLogResponse carries one page of audit entries, newest first.
| Field | Type | Description |
|---|---|---|
| entries | repeated AuditEntry | The entries, newest first |
| next_cursor | string | Cursor for the next page; empty when this is the last page |
CheckForUpdateRequest
CheckForUpdateRequest is empty; the feed is server configuration.
CheckForUpdateResponse
CheckForUpdateResponse compares the running version with the newest published release.
| Field | Type | Description |
|---|---|---|
| current_version | string | The version this server is running |
| latest_version | string | The newest published version the feed reports |
| update_available | bool | True when latest_version is newer than current_version |
| release_url | string | Human-readable page for the newest release (empty when the feed has none) |
ExportDataRequest
ExportDataRequest names the archive to build.
| Field | Type | Description |
|---|---|---|
| mode | ExportMode | Which archive to build; UNSPECIFIED is rejected |
ExportDataResponse
ExportDataResponse carries the finished archive.
| Field | Type | Description |
|---|---|---|
| archive | bytes | gzip-compressed JSON envelope (formatVersion, exportedAt, mode, appVersion, tables). Opaque to the response interceptors by design (ADR-0022). |
| filename | string | Suggested download name, e.g. eagraiclainne-backup-2026-08-08T12-00-00Z.json.gz |
ExportUserDataRequest
ExportUserDataRequest names whose data to export.
| Field | Type | Description |
|---|---|---|
| user_uid | string | UID of the member to export. Empty means the caller themselves; a non-self target requires ADMIN (enforced in the handler, ADR-0027). |
GetSystemSettingsRequest
GetSystemSettingsRequest is empty; the settings are global.
GetSystemSettingsResponse
GetSystemSettingsResponse carries the readable settings scalars. The JWT secret is deliberately not part of this message.
| Field | Type | Description |
|---|---|---|
| family_name | string | Household display name |
| token_expiry_seconds | int64 | Lifetime of newly issued login tokens, in seconds |
| default_locale | string | Family-wide default locale (BCP-47 tag); empty means English |
| mcp_enabled | bool | Whether the /mcp endpoint is enabled |
ImportDataRequest
ImportDataRequest carries the archive to restore.
| Field | Type | Description |
|---|---|---|
| archive | bytes | gzipped backup envelope produced by ExportData; backup mode only โ portable archives carry no credentials and are refused |
| wipe | bool | Consent to replace existing data. Required once any user exists; ignored on an empty install. |
ImportDataResponse
ImportDataResponse reports the restore and, after a wipe, carries the replaced data.
| Field | Type | Description |
|---|---|---|
| pre_wipe_archive | bytes | Backup of the data that existed before a wipe, read inside the import transaction. Empty on a setup-window restore. Opaque bytes by design โ ADR-0022's sanitize bypass, extended by ADR-0023. |
| pre_wipe_filename | string | Suggested filename for the pre-wipe backup; empty when no wipe happened |
| rows_restored | int64 | Total rows restored across all tables |
RotateJWTSecretRequest
RotateJWTSecretRequest is empty: the server generates the new secret; callers never supply or see it.
SetupStatusRequest
SetupStatusRequest is empty; setup state is global.
SetupStatusResponse
SetupStatusResponse describes the install's setup state.
| Field | Type | Description |
|---|---|---|
| needs_setup | bool | True when no users exist yet |
| setup_token_required | bool | True when EAG_INITIAL_ADMIN_TOKEN is configured on the server |
UpdateSystemSettingsRequest
UpdateSystemSettingsRequest names the settings to change; absent fields are left untouched.
| Field | Type | Description |
|---|---|---|
| family_name | string | New household display name |
| token_expiry_seconds | int64 | New lifetime for newly issued login tokens, in seconds. Affects new sign-ins only; outstanding tokens keep their embedded expiry. |
| default_locale | string | New family-wide default locale (BCP-47 tag, one of the supported set) |
| mcp_enabled | bool | Turn the /mcp endpoint on or off. Takes effect on the next request. |
ExportMode
ExportMode selects what an export archive contains.
| Value | Description |
|---|---|
| EXPORT_MODE_UNSPECIFIED | No mode chosen; the server rejects this โ secrets must never ship by default. |
| EXPORT_MODE_BACKUP | Full fidelity: every table verbatim, secrets included. Restore-capable. |
| EXPORT_MODE_PORTABLE | Sanitized: content tables only, credential and session material stripped. Safe to keep or hand over. |
User
UserService
UserService provides operations for managing users
| RPC | Request | Response | Description |
|---|---|---|---|
| Create | CreateUserRequest | UserResponse | Create a new user |
| Read | UserRequest | UserResponse | Read an existing user by UID |
| Update | UpdateUserRequest | UserResponse | Update an existing user |
| Delete | UserRequest | SuccessResponse | Delete a user by UID |
| ReferenceUser | ReferenceUserRequest | UserResponse | Add a reference/relationship to another user |
| Login | LoginRequest | LoginResponse | Authenticate a user with email and password |
| RenewToken | RenewTokenRequest | LoginResponse | Re-issue the requester's token with a fresh expiry (sliding renewal). Roles come from the stored account, not the presented token, so a renewal also picks up role changes made since sign-in. Requires a currently valid token โ an expired session cannot renew itself. |
| AssignRoles | AssignRolesRequest | AssignRolesResponse | Assign roles to a user (ADMIN only) |
| Logout | LogoutRequest | SuccessResponse | Clear the web session cookie. Public: it must work even when the presented session is already dead. Clears the cookie only โ it does not invalidate the token elsewhere. |
| RefreshSession | RefreshSessionRequest | LoginResponse | Exchange a valid refresh token for a fresh access token and a new refresh token (rotation: the presented token is dead afterwards). Public: the access token is typically expired when this is called. Presenting a rotated-out token revokes the whole session chain โ the reuse means either theft or a client that lost a rotation. |
| ListSessions | ListSessionsRequest | ListSessionsResponse | List the requester's own device sessions (one entry per chain, newest first). Token hashes never leave the server. |
| RevokeSession | RevokeSessionRequest | SuccessResponse | Revoke one of the requester's own session chains: every refresh token in the chain is dead on the next exchange. Idempotent. |
| ChangePassword | ChangePasswordRequest | SuccessResponse | Change the requester's own password (proving the current one) |
| SetPassword | SetPasswordRequest | SuccessResponse | Set another user's password without proof of the current one (ADMIN only) โ the recovery path when a member forgets theirs. |
| SwitchProfile | SwitchProfileRequest | LoginResponse | Swap the device's active session to another profile whose parked refresh chain this device holds (ADR-0020). Public: the caller's access token may be absent (sign-in screen) โ possession of the parked chain is the base proof, plus the target's PIN when one is set. |
| SetPin | SetPinRequest | SuccessResponse | Set or clear the requester's own quick-switch PIN, proving the current password. Strictly self-service โ the request carries no uid by design. |
| ClearPin | ClearPinRequest | SuccessResponse | Clear another member's quick-switch PIN (ADMIN only) โ the recovery path when a member forgets theirs. PINs are cleared, never read. |
| ListUsers | ListUsersRequest | ListUsersResponse | List all users |
AssignRolesRequest
AssignRolesRequest contains the data needed to assign roles to a user
| Field | Type | Description |
|---|---|---|
| user_uid | string | UID of the user to assign roles to |
| roles | repeated Role | Roles to assign to the user |
AssignRolesResponse
AssignRolesResponse returns the updated user with new roles
| Field | Type | Description |
|---|---|---|
| user | User | The updated user |
ChangePasswordRequest
ChangePasswordRequest carries a self-service password change: the caller proves the current password and supplies the new one. The account is always the requester's own โ there is no uid field by design.
| Field | Type | Description |
|---|---|---|
| current_password | string | The account's current password |
| new_password | string | The new password (minimum length enforced server-side) |
ClearPinRequest
ClearPinRequest carries an admin PIN reset: another member's PIN is cleared, never read. The member sets a new one themselves via SetPin.
| Field | Type | Description |
|---|---|---|
| user_uid | string | UID of the account to clear the PIN on |
CreateUserRequest
CreateUserRequest contains the data needed to create a new user
| Field | Type | Description |
|---|---|---|
| user | User | User data for the new user |
| password | string | Plain text password (will be hashed server-side) |
| initial_roles | repeated Role | Initial roles to assign (only ADMIN can set, defaults to [ROLE_MEMBER]) |
ListSessionsRequest
ListSessionsRequest is empty: sessions listed are the requester's own.
ListSessionsResponse
ListSessionsResponse returns the requester's device sessions, one per chain, newest first. Token hashes are scrubbed.
| Field | Type | Description |
|---|---|---|
| sessions | repeated Session | The session records |
ListUsersRequest
ListUsersRequest contains optional filters for listing users
| Field | Type | Description |
|---|---|---|
| limit | int32 | Optional maximum number of users to return (0 means no limit) |
ListUsersResponse
ListUsersResponse returns a list of users
| Field | Type | Description |
|---|---|---|
| users | repeated User | The list of users |
LoginRequest
LoginRequest contains credentials for authentication
| Field | Type | Description |
|---|---|---|
| string | Email address | |
| password | string | Plain text password |
| remember_device | bool | Ask for a refresh token alongside the access token, starting a device session that outlives the access token's expiry. |
| device_label | string | Human-readable device label for the session list, e.g. "Luke's phone". Only meaningful with remember_device. |
LoginResponse
LoginResponse returns authentication token and user info
| Field | Type | Description |
|---|---|---|
| token | string | JWT token for authentication |
| expires_at | google.protobuf.Timestamp | Token expiration time |
| user | User | Authenticated user information |
| refresh_token | string | Opaque refresh token (remember_device logins and RefreshSession only). Shown exactly once per rotation; the server stores a hash. Blanked in cookie mode โ the browser gets it as an httpOnly cookie scoped to the RefreshSession procedure instead. |
LogoutRequest
LogoutRequest ends the caller's session. Browsers send it empty โ the refresh cookie names the chain to revoke. Bearer clients (the app) have no cookie and pass their refresh token here instead.
| Field | Type | Description |
|---|---|---|
| refresh_token | string | The opaque refresh token whose chain this logout revokes (bearer clients; cookie mode leaves it empty) |
| forget_user_uid | string | Forget this user's PARKED chain on this device instead of the active session (ADR-0020): the parked chain is revoked and its cookie cleared, the active session untouched. Possession of the parked cookie is the authority. The server half of "Remove me from this device". |
ReferenceUserRequest
ReferenceUserRequest contains the data needed to add a relationship reference
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the user to add the reference to |
| ref | UserRef | Reference/relationship to add |
RefreshSessionRequest
RefreshSessionRequest presents the opaque refresh token. Empty in cookie mode: the browser's httpOnly refresh cookie carries it instead.
| Field | Type | Description |
|---|---|---|
| refresh_token | string | The opaque refresh token (bearer clients; cookie mode leaves it empty) |
RenewTokenRequest
RenewTokenRequest is empty: the account is always the requester's own, identified by the presented token.
RevokeSessionRequest
RevokeSessionRequest names one of the requester's chains to kill.
| Field | Type | Description |
|---|---|---|
| chain_uid | string | chain_uid of the session to revoke |
Session
Session is one refresh-token record. Rotation writes a new record into the same chain, so a device session is a chain of records; the newest unrevoked one holds the only exchangeable token. Rows are server-side bookkeeping โ ListSessions returns one Session per chain with the hash scrubbed.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier of this record |
| user_uid | string | The account the session belongs to |
| label | string | Human-readable device label, from the login that started the chain |
| token_hash | string | SHA-256 of the opaque token. Never returned in API responses (response-scrubbed); the plaintext token is never stored. |
| chain_uid | string | Chain identity: constant across rotations of one device session |
| created_at | google.protobuf.Timestamp | When this record was minted |
| last_used_at | google.protobuf.Timestamp | When this record's token was last presented |
| revoked_at | google.protobuf.Timestamp | Set when rotated out or revoked. A revoked record's token no longer exchanges โ presenting it anyway revokes the whole chain. |
| successor_uid | string | Set when the revocation was a rotation: the uid of the record this one rotated into. Absent on explicitly revoked records โ the distinction gates the reuse grace (a race loser gets grace; a revoked session gets refused on the spot). |
| pin_misses | int32 | Consecutive failed quick-switch PIN attempts against this chain (ADR-0020). Copied to the successor on rotation, like label. The fifth miss locks the chain for PIN switching. |
| pin_locked_at | google.protobuf.Timestamp | Set when the fifth miss locked this chain for PIN switching. Only a fresh password login clears it โ the new chain replaces this one. |
| pin_attempted_at | google.protobuf.Timestamp | When a PIN was last attempted against this chain, for attempt-rate throttling. |
SetPasswordRequest
SetPasswordRequest carries an admin password reset: a temporary password set on another member's account, no current-password proof. The member is expected to change it themselves via ChangePassword afterwards; nothing forces that (household trust model).
| Field | Type | Description |
|---|---|---|
| user_uid | string | UID of the account to set the password on |
| new_password | string | The new password (minimum length enforced server-side) |
SetPinRequest
SetPinRequest carries a self-service PIN change: the caller proves the current password and supplies the new PIN. The account is always the requester's own โ there is no uid field by design.
| Field | Type | Description |
|---|---|---|
| current_password | string | The account's current password |
| pin | string | The new PIN โ exactly 4 digits. Empty clears the caller's PIN. |
SwitchProfileRequest
SwitchProfileRequest asks to make another resident profile the device's
active session (ADR-0020). In cookie mode the parked chains ride httpOnly
eagraiclainne_parked_
| Field | Type | Description |
|---|---|---|
| target_user_uid | string | UID of the profile to switch to |
| pin | string | The target's 4-digit quick-switch PIN. Required when the target has one; ignored otherwise. |
| target_refresh_token | string | The target's parked refresh token (bearer clients; cookie mode leaves it empty โ the eagraiclainne_parked_ |
| park_refresh_token | string | The caller's own active refresh token, to be parked โ or revoked, when it belongs to a PIN-less admin (bearer clients; cookie mode leaves it empty โ the eagraiclainne_refresh cookie carries it) |
UpdateUserRequest
UpdateUserRequest contains the data needed to update an existing user
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the user to update |
| user | User | Updated user data |
| update_mask | google.protobuf.FieldMask | Names of the User fields to apply from user. Must name at least one field; unmasked fields are left untouched. Members updating their own profile may mask name, email, birthday and settings; ADMIN may additionally mask deathday and relations. Masking "settings" replaces the whole message (no sub-paths); masking it with settings absent clears the stored settings. |
User
User represents a person in the family organization system
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier for the user |
| name | string | Full name of the user |
| birthday | google.protobuf.Timestamp | Date of birth |
| deathday | google.protobuf.Timestamp | Date of death (optional, for deceased family members) |
| mutations | repeated Mutation | Audit trail of changes made to this user |
| string | Email address of the user | |
| relations | repeated UserRef | Family relationships to other users. Stored edges only: PARENT (directed child -> parent, stored on the child), PARTNER (symmetric, stored once) and EX_PARTNER (inert). See ADR-0004. |
| password_hash | string | Password hash (bcrypt) - never returned in API responses |
| roles | repeated Role | User's assigned roles (determines permissions) |
| derived_relations | repeated UserRef | Derived family relations (CHILD, SIBLING, GRANDPARENT, GRANDCHILD, AUNT_UNCLE, COUSIN, PARENT_IN_LAW, CHILD_IN_LAW), computed server-side from the stored PARENT and PARTNER edges on every read (ADR-0004). Response-only: never stored and never writable. |
| settings | UserSettings | Synced appearance settings: mark and theme. Visible to every member โ the mark is shared identity โ and writable via the Update mask path "settings" (self, or ADMIN for members who cannot sign in). |
| token_generation | int64 | Credential generation, bumped on every password change so tokens minted before the change stop verifying. Never returned in API responses (response-scrubbed) and never client-writable. |
| pin_hash | string | bcrypt hash of the 4-digit quick-switch PIN (ADR-0020). Empty means no PIN is set. Never returned in API responses (response-scrubbed). |
| has_pin | bool | Whether a quick-switch PIN is set. Response-only: derived from pin_hash by the sanitize pass before the hash is scrubbed โ never stored, never client-writable. |
UserMark
UserMark is a member's shared identity mark: a colour id and a symbol id. The web owns the id vocabularies; the server only caps lengths.
| Field | Type | Description |
|---|---|---|
| color | string | Colour id (Okabe-Ito palette id in the web client) |
| symbol | string | Symbol id (emoji symbol id in the web client) |
UserRequest
Common request/response patterns UserRequest is used for operations that only need a user UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the user |
UserResponse
UserResponse returns a user object with its UID
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the user |
| user | User | The user data |
UserSettings
UserSettings carries the synced per-member preferences.
| Field | Type | Description |
|---|---|---|
| mark | UserMark | The member's mark, rendered for everyone |
| theme | UserTheme | The member's theme preferences |
| tour_seen_version | int32 | Highest first-login tour version this member has seen (0 = never). The web shows the walkthrough when this trails its current version, so a member sees each tour once across every device โ and future releases can append "what's new" cards by bumping the version. |
| locale | string | BCP-47 language tag for the member's UI and notifications ("en", "ga", "fr", "de", "es", "pl"). Empty means "use the family default". |
| push_disabled_kinds | repeated NotificationKind | Notification kinds Web Push delivery is switched OFF for. Absent means every kind pushes. Gates push only โ the in-app inbox always gets every row. |
UserTheme
UserTheme is a member's synced appearance: theme family, light/dark/auto mode, and the custom theme's token overrides plus the hand-pinned token list. Text scale and reduce-motion are deliberately device-local and not synced here.
| Field | Type | Description |
|---|---|---|
| family | string | Theme family id, or "custom" |
| mode | string | "light", "dark" or "auto" |
| custom_tokens | map<string, string> | Custom theme token overrides (CSS custom property name -> value) |
| custom_touched | repeated string | Token names the user pinned by hand in the custom theme editor |
Webhook
WebhookService
WebhookService manages outbound event targets (ADR-0024). Every RPC is admin-only via the permission matrix (ADR-0002/0008).
| RPC | Request | Response | Description |
|---|---|---|---|
| CreateWebhook | CreateWebhookRequest | CreateWebhookResponse | Register a webhook. The response carries the signing secret exactly once. |
| ListWebhooks | ListWebhooksRequest | ListWebhooksResponse | List all webhooks (records only โ no secrets) |
| UpdateWebhook | UpdateWebhookRequest | WebhookResponse | Replace a webhook's name, URL and event patterns |
| DeleteWebhook | WebhookRequest | DeleteWebhookResponse | Remove a webhook; in-flight deliveries stop at their next retry |
| TestWebhook | WebhookRequest | TestWebhookResponse | Send a signed webhook.test event synchronously and report the outcome |
CreateWebhookRequest
CreateWebhookRequest registers a new outbound target (ADMIN only).
| Field | Type | Description |
|---|---|---|
| name | string | Human-readable label (required) |
| url | string | Delivery target (required; http or https) |
| events | repeated string | Event patterns to subscribe to |
CreateWebhookResponse
CreateWebhookResponse carries the record and โ exactly once โ the raw signing secret. It cannot be retrieved again; rotation is delete-and-recreate.
| Field | Type | Description |
|---|---|---|
| webhook | Webhook | The stored webhook record (secret scrubbed) |
| secret | string | The HMAC signing secret; shown only in this response |
DeleteWebhookResponse
DeleteWebhookResponse confirms removal.
ListWebhooksRequest
ListWebhooksRequest lists every webhook.
| Field | Type | Description |
|---|---|---|
| limit | int32 | Maximum number of webhooks to return (0 = no limit) |
ListWebhooksResponse
ListWebhooksResponse returns webhook records (secrets scrubbed).
| Field | Type | Description |
|---|---|---|
| webhooks | repeated Webhook | The webhook records |
TestWebhookResponse
TestWebhookResponse reports the outcome of a synchronous test delivery.
| Field | Type | Description |
|---|---|---|
| status_code | int32 | HTTP status returned by the target (0 when the request never completed) |
| latency_ms | int64 | Round-trip time of the delivery attempt |
| error | string | Transport error when the request never completed; empty on success |
UpdateWebhookRequest
UpdateWebhookRequest replaces a webhook's name, URL and event patterns in full. The secret is never changed here โ rotation is delete-and-recreate.
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the webhook |
| name | string | New label (required) |
| url | string | New delivery target (required) |
| events | repeated string | New event patterns (full replace) |
Webhook
Webhook is an admin-configured outbound event target (ADR-0024). Matching events are POSTed to the URL as signed JSON envelopes. The secret keys the HMAC signature and is stored on this record because signing needs the raw value; the sanitize scrub map clears it from every response โ it appears in clear exactly once, as CreateWebhookResponse.secret.
| Field | Type | Description |
|---|---|---|
| uid | string | Unique identifier |
| name | string | Human-readable label, e.g. "home-assistant" |
| url | string | Delivery target; http or https |
| events | repeated string | Event patterns this webhook subscribes to. Exact match, or prefix when the pattern ends in "" ("notify.", "mutation.item.", bare ""). Empty = deliver nothing. |
| secret | string | Raw HMAC-SHA256 key. Stored for signing; scrubbed from every response. |
| created_by | UserRef | Admin who created the webhook |
| created_at | google.protobuf.Timestamp | When the webhook was created |
| last_delivery_status | string | Outcome of the most recent delivery ("delivered" or "failed: |
| last_delivery_at | google.protobuf.Timestamp | When the most recent delivery finished |
| mutations | repeated Mutation | Audit trail of changes made to this webhook |
WebhookRequest
WebhookRequest names one webhook by uid.
| Field | Type | Description |
|---|---|---|
| uid | string | UID of the webhook |
WebhookResponse
WebhookResponse returns one webhook record.
| Field | Type | Description |
|---|---|---|
| webhook | Webhook | The webhook record |