M12e: contract pass (pair invite kind, admin name in invite links, sync failed flag, rejection actor, device version name, template id, update check result, untouched accounts, D-027 re-stamp, PushEvent in :core:model, snackbar hosts)

Server: E-3 invite_kind; E-12 link ?from=<admin name>, E-4 passes it into kvitt://invite;
X-Kvitt-App-Version-Name stored in devices.app_version_name (migration 0003, schema 3);
OpenAPI and examples follow. Phone: :core:model, :core:fakes, :data, features and :app
updated together; goldens re-recorded for S-20 (version name) and new S-1/S-22 states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-18 15:00:50 +02:00
parent 6159e18198
commit 8c607153c0
96 changed files with 1090 additions and 298 deletions
+55 -3
View File
@@ -36,6 +36,7 @@ paths:
parameters:
- $ref: '#/components/parameters/ProtocolOptional'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -74,6 +75,7 @@ paths:
parameters:
- $ref: '#/components/parameters/ProtocolOptional'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -100,6 +102,7 @@ paths:
parameters:
- $ref: '#/components/parameters/ProtocolOptional'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -148,8 +151,15 @@ paths:
required: true
schema:
type: string
- name: from
in: query
required: false
description: The inviting admin's display name (E-12 link), passed on into the `kvitt://invite` link; an invalid value is ignored.
schema:
type: string
- $ref: '#/components/parameters/ProtocolOptional'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -185,6 +195,7 @@ paths:
parameters:
- $ref: '#/components/parameters/ProtocolOptional'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -220,6 +231,7 @@ paths:
type: string
- $ref: '#/components/parameters/ProtocolOptional'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -253,6 +265,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -280,6 +293,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -322,6 +336,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -374,6 +389,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -418,6 +434,7 @@ paths:
type: integer
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -465,6 +482,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -474,7 +492,7 @@ paths:
$ref: '#/components/schemas/CreateInviteRequest'
responses:
'201':
description: The invite with `code` (12 characters, no hyphens) and `link` (`<public_url>/i/<code>`).
description: The invite with `code` (12 characters, no hyphens) and `link` (`<public_url>/i/<code>?from=<admin display name, percent-encoded>`).
headers:
X-Kvitt-Server-Version:
$ref: '#/components/headers/ServerVersion'
@@ -510,6 +528,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -544,6 +563,7 @@ paths:
- $ref: '#/components/parameters/InviteId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'204':
@@ -575,6 +595,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -606,6 +627,7 @@ paths:
- $ref: '#/components/parameters/DeviceId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'204':
@@ -640,6 +662,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -674,6 +697,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -719,6 +743,7 @@ paths:
- $ref: '#/components/parameters/AccountId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -766,6 +791,7 @@ paths:
- $ref: '#/components/parameters/AccountId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -803,6 +829,7 @@ paths:
- $ref: '#/components/parameters/AccountId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -837,6 +864,7 @@ paths:
- $ref: '#/components/parameters/AccountId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -870,6 +898,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -915,6 +944,7 @@ paths:
- $ref: '#/components/parameters/CategoryId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -961,6 +991,7 @@ paths:
- $ref: '#/components/parameters/CategoryId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -998,6 +1029,7 @@ paths:
- $ref: '#/components/parameters/CategoryId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -1032,6 +1064,7 @@ paths:
- $ref: '#/components/parameters/CategoryId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -1065,6 +1098,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -1111,6 +1145,7 @@ paths:
- $ref: '#/components/parameters/TemplateId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -1154,6 +1189,7 @@ paths:
- $ref: '#/components/parameters/TemplateId'
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -1202,6 +1238,7 @@ paths:
default: 500
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
responses:
'200':
@@ -1236,6 +1273,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -1284,6 +1322,7 @@ paths:
parameters:
- $ref: '#/components/parameters/Protocol'
- $ref: '#/components/parameters/AppVersion'
- $ref: '#/components/parameters/AppVersionName'
- $ref: '#/components/parameters/AcceptLanguage'
requestBody:
required: true
@@ -1355,6 +1394,13 @@ components:
description: The app's `versionCode`, sent by the app on every call.
schema:
type: integer
AppVersionName:
name: X-Kvitt-App-Version-Name
in: header
required: false
description: The app's `versionName`, sent by the app on every call; stored per device like `X-Kvitt-App-Version` (1 to 32 characters, anything else ignored).
schema:
type: string
AcceptLanguage:
name: Accept-Language
in: header
@@ -1650,7 +1696,7 @@ components:
Device:
type: object
description: Only in /me, /devices and /invites/accept.
required: [id, user_id, name, app_version_code, created_at, last_seen_at, revoked_at, is_current]
required: [id, user_id, name, app_version_code, app_version_name, created_at, last_seen_at, revoked_at, is_current]
properties:
id:
$ref: '#/components/schemas/Uuid'
@@ -1660,6 +1706,9 @@ components:
type: string
app_version_code:
type: [integer, 'null']
app_version_name:
type: [string, 'null']
description: The app's `versionName` from `X-Kvitt-App-Version-Name`; null until a request carried it.
created_at:
$ref: '#/components/schemas/Timestamp'
last_seen_at:
@@ -2026,11 +2075,14 @@ components:
description: Required for `admin_setup` and `partner` invites (`invalid_request` if missing), ignored for `device`.
AcceptInviteResponse:
type: object
required: [token, user, device, household]
required: [token, invite_kind, user, device, household]
properties:
token:
type: string
description: '`kvt_` + base64url without padding of 32 random bytes (47 characters, SPEC §8.2).'
invite_kind:
$ref: '#/components/schemas/InviteKind'
description: The kind of the accepted invite; the app shows the profile step only after `partner` (AC-2.10).
user:
$ref: '#/components/schemas/User'
device: