@thinkai/tai-api-contract 2.69.0 → 2.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  openapi: 3.0.3
2
2
  info:
3
3
  title: ThinkAI API
4
- version: 2.69.0
4
+ version: 2.71.0
5
5
  description: >
6
6
  Contract surface for the AI Driven SDLC backend used by ThinkAI.
7
7
  Workspace-scoped routes use `/workspaces/{workspaceId}/...`.
@@ -968,11 +968,15 @@ paths:
968
968
  tags: [Workspace]
969
969
  summary: Invite member to workspace
970
970
  description: >
971
- Workspace admin only. Upserts `tai_users` by email and adds or reactivates
972
- `tai_workspace_members`. Idempotent when the email is already an active member:
973
- returns **201** with the existing row and **does not change** their role if the
974
- request body role differs. Does not send email; optional `tai_workspace_invitations`
975
- row may be recorded (no accept/link flow yet).
971
+ Workspace admin only. Creates or refreshes a **pending**
972
+ `tai_workspace_invitations` row and emails an accept link; it does **not** create
973
+ `tai_workspace_members`. Membership is granted only by
974
+ `POST /invitations/{token}/accept`. Applies to new and existing platform users
975
+ alike. Idempotent when the email is already an active member: returns **201** with
976
+ `outcome: "already_member"` and the existing row, **does not change** their role if
977
+ the request body role differs, and sends no email. Re-inviting a pending email
978
+ refreshes the token and 7-day expiry (the previous link stops working). When SMTP
979
+ is not configured the invitation is still created and a warning is logged.
976
980
  operationId: inviteTenantMember
977
981
  parameters:
978
982
  - $ref: "#/components/parameters/WorkspaceId"
@@ -987,11 +991,33 @@ paths:
987
991
  role: editor
988
992
  responses:
989
993
  "201":
990
- description: Invited member row
994
+ description: Pending invitation, or the existing member row when already a member
991
995
  content:
992
996
  application/json:
993
997
  schema:
994
- $ref: "#/components/schemas/TeamMemberDto"
998
+ $ref: "#/components/schemas/InviteTeamMemberResultDto"
999
+ examples:
1000
+ invited:
1001
+ value:
1002
+ outcome: invited
1003
+ invitation:
1004
+ id: 6f1d9c0e-4d0b-4a0f-9a3e-2c1b8f4a7d55
1005
+ workspaceId: 9a2b7c1d-3e4f-4a5b-8c9d-0e1f2a3b4c5d
1006
+ workspaceName: Acme Engineering
1007
+ email: new.member@example.com
1008
+ role: editor
1009
+ status: pending
1010
+ invitedByName: Dana Admin
1011
+ expiresAt: "2026-08-01T12:00:00.000Z"
1012
+ createdAt: "2026-07-25T12:00:00.000Z"
1013
+ alreadyMember:
1014
+ value:
1015
+ outcome: already_member
1016
+ member:
1017
+ id: 1b2c3d4e-5f60-4718-8293-a4b5c6d7e8f9
1018
+ name: Existing Member
1019
+ email: new.member@example.com
1020
+ role: admin
995
1021
  "400":
996
1022
  description: Invalid email or role
997
1023
  content:
@@ -1099,6 +1125,175 @@ paths:
1099
1125
  error: Cannot remove the workspace owner
1100
1126
  code: workspace_owner
1101
1127
 
1128
+ /workspaces/{workspaceId}/invitations:
1129
+ get:
1130
+ tags: [Workspace]
1131
+ summary: List pending workspace invitations
1132
+ description: >
1133
+ Workspace admin only. Returns invitations still awaiting a decision so the team
1134
+ settings page can show "pending" alongside active members. Rows whose `expiresAt`
1135
+ has passed are flipped to `expired` on read and omitted.
1136
+ operationId: listWorkspaceInvitations
1137
+ parameters:
1138
+ - $ref: "#/components/parameters/WorkspaceId"
1139
+ responses:
1140
+ "200":
1141
+ description: Pending invitations
1142
+ content:
1143
+ application/json:
1144
+ schema:
1145
+ $ref: "#/components/schemas/WorkspaceInvitationListDto"
1146
+ "401":
1147
+ $ref: "#/components/responses/Unauthorized"
1148
+ "403":
1149
+ $ref: "#/components/responses/Forbidden"
1150
+ "404":
1151
+ description: Workspace does not exist or malformed workspaceId
1152
+ content:
1153
+ application/json:
1154
+ schema:
1155
+ $ref: "#/components/schemas/ErrorMessageDto"
1156
+
1157
+ /invitations/{token}:
1158
+ get:
1159
+ tags: [Workspace]
1160
+ summary: Preview a workspace invitation (public)
1161
+ description: >
1162
+ **Unauthenticated.** Lets the SPA render the invite landing page before the invitee
1163
+ has signed in. The invited email is **masked** (`j••••e@example.com`) so a leaked
1164
+ link does not disclose an address. Returns `404` with a generic message for unknown,
1165
+ revoked, or rejected tokens; a past-due `pending` invitation is flipped to `expired`
1166
+ and returned with that status so the SPA can show a stable message.
1167
+ operationId: getWorkspaceInvitationPreview
1168
+ security: []
1169
+ parameters:
1170
+ - $ref: "#/components/parameters/WorkspaceInvitationToken"
1171
+ responses:
1172
+ "200":
1173
+ description: Invitation preview
1174
+ content:
1175
+ application/json:
1176
+ schema:
1177
+ $ref: "#/components/schemas/WorkspaceInvitationPreviewDto"
1178
+ "404":
1179
+ description: Unknown or no longer available invitation
1180
+ content:
1181
+ application/json:
1182
+ schema:
1183
+ $ref: "#/components/schemas/ErrorMessageDto"
1184
+ examples:
1185
+ notFound:
1186
+ value:
1187
+ error: This invitation link is not valid.
1188
+ code: invitation_not_found
1189
+
1190
+ /invitations/{token}/accept:
1191
+ post:
1192
+ tags: [Workspace]
1193
+ summary: Accept a workspace invitation
1194
+ description: >
1195
+ Requires a Supabase JWT whose email matches the invited email. Creates the
1196
+ `tai_workspace_members` row with the invited role and closes the invitation as
1197
+ `accepted`. This is the **only** path that grants workspace membership — signing up
1198
+ alone never does. Idempotent: re-accepting your own accepted invitation returns
1199
+ `200` unchanged.
1200
+ operationId: acceptWorkspaceInvitation
1201
+ parameters:
1202
+ - $ref: "#/components/parameters/WorkspaceInvitationToken"
1203
+ responses:
1204
+ "200":
1205
+ description: Invitation accepted; caller is now a workspace member
1206
+ content:
1207
+ application/json:
1208
+ schema:
1209
+ $ref: "#/components/schemas/WorkspaceInvitationDto"
1210
+ "401":
1211
+ $ref: "#/components/responses/Unauthorized"
1212
+ "403":
1213
+ description: Signed-in email does not match the invited email
1214
+ content:
1215
+ application/json:
1216
+ schema:
1217
+ $ref: "#/components/schemas/ErrorMessageDto"
1218
+ examples:
1219
+ emailMismatch:
1220
+ value:
1221
+ error: This invitation was sent to a different email address.
1222
+ code: invitation_email_mismatch
1223
+ "404":
1224
+ description: Unknown invitation token
1225
+ content:
1226
+ application/json:
1227
+ schema:
1228
+ $ref: "#/components/schemas/ErrorMessageDto"
1229
+ "409":
1230
+ description: Invitation is no longer actionable
1231
+ content:
1232
+ application/json:
1233
+ schema:
1234
+ $ref: "#/components/schemas/ErrorMessageDto"
1235
+ examples:
1236
+ notPending:
1237
+ value:
1238
+ error: This invitation is no longer available.
1239
+ code: invitation_not_pending
1240
+ "410":
1241
+ description: Invitation expired
1242
+ content:
1243
+ application/json:
1244
+ schema:
1245
+ $ref: "#/components/schemas/ErrorMessageDto"
1246
+ examples:
1247
+ expired:
1248
+ value:
1249
+ error: This invitation has expired. Ask an admin to send a new one.
1250
+ code: invitation_expired
1251
+
1252
+ /invitations/{token}/reject:
1253
+ post:
1254
+ tags: [Workspace]
1255
+ summary: Decline a workspace invitation
1256
+ description: >
1257
+ Requires a Supabase JWT whose email matches the invited email — anonymous decline
1258
+ with only the token is not allowed. Closes the invitation as `rejected` and creates
1259
+ no member row. Idempotent for an already-rejected invitation.
1260
+ operationId: rejectWorkspaceInvitation
1261
+ parameters:
1262
+ - $ref: "#/components/parameters/WorkspaceInvitationToken"
1263
+ responses:
1264
+ "200":
1265
+ description: Invitation declined
1266
+ content:
1267
+ application/json:
1268
+ schema:
1269
+ $ref: "#/components/schemas/WorkspaceInvitationDto"
1270
+ "401":
1271
+ $ref: "#/components/responses/Unauthorized"
1272
+ "403":
1273
+ description: Signed-in email does not match the invited email
1274
+ content:
1275
+ application/json:
1276
+ schema:
1277
+ $ref: "#/components/schemas/ErrorMessageDto"
1278
+ "404":
1279
+ description: Unknown invitation token
1280
+ content:
1281
+ application/json:
1282
+ schema:
1283
+ $ref: "#/components/schemas/ErrorMessageDto"
1284
+ "409":
1285
+ description: Invitation was already accepted
1286
+ content:
1287
+ application/json:
1288
+ schema:
1289
+ $ref: "#/components/schemas/ErrorMessageDto"
1290
+ "410":
1291
+ description: Invitation expired
1292
+ content:
1293
+ application/json:
1294
+ schema:
1295
+ $ref: "#/components/schemas/ErrorMessageDto"
1296
+
1102
1297
  /workspaces/{workspaceId}/org-chart/status:
1103
1298
  get:
1104
1299
  tags: [Workspace]
@@ -7700,6 +7895,16 @@ components:
7700
7895
  type: string
7701
7896
  format: uuid
7702
7897
  description: Workspace member user id (`tai_users.id`).
7898
+ WorkspaceInvitationToken:
7899
+ name: token
7900
+ in: path
7901
+ required: true
7902
+ schema:
7903
+ type: string
7904
+ minLength: 32
7905
+ description: >
7906
+ Opaque workspace invitation token from the emailed accept link. Only its SHA-256
7907
+ hash is stored server-side; the plaintext is shown exactly once, in the email.
7703
7908
  PrivacyRequestId:
7704
7909
  name: requestId
7705
7910
  in: path
@@ -8029,6 +8234,100 @@ components:
8029
8234
  type: string
8030
8235
  enum: [admin, editor, viewer]
8031
8236
 
8237
+ WorkspaceInvitationStatusDto:
8238
+ type: string
8239
+ description: >
8240
+ `revoked` is an admin cancelling the invitation; `rejected` is the invitee
8241
+ declining it. Both leave the invitee outside the workspace.
8242
+ enum: [pending, accepted, expired, revoked, rejected]
8243
+
8244
+ WorkspaceInvitationDto:
8245
+ type: object
8246
+ description: Workspace invitation as seen by an authenticated admin or invitee.
8247
+ required:
8248
+ [id, workspaceId, workspaceName, email, role, status, expiresAt, createdAt]
8249
+ properties:
8250
+ id:
8251
+ type: string
8252
+ format: uuid
8253
+ workspaceId:
8254
+ type: string
8255
+ format: uuid
8256
+ workspaceName:
8257
+ type: string
8258
+ email:
8259
+ type: string
8260
+ description: Invited email address.
8261
+ role:
8262
+ type: string
8263
+ enum: [admin, editor, viewer]
8264
+ status:
8265
+ $ref: "#/components/schemas/WorkspaceInvitationStatusDto"
8266
+ invitedByName:
8267
+ type: string
8268
+ nullable: true
8269
+ invitedByEmail:
8270
+ type: string
8271
+ nullable: true
8272
+ expiresAt:
8273
+ type: string
8274
+ format: date-time
8275
+ createdAt:
8276
+ type: string
8277
+ format: date-time
8278
+ acceptedAt:
8279
+ type: string
8280
+ format: date-time
8281
+ nullable: true
8282
+
8283
+ WorkspaceInvitationPreviewDto:
8284
+ type: object
8285
+ description: >
8286
+ Unauthenticated invitation preview. `maskedEmail` is the only view of the invited
8287
+ address; compare it to the signed-in account client-side before calling accept.
8288
+ required: [workspaceName, role, status, maskedEmail, expiresAt]
8289
+ properties:
8290
+ workspaceName:
8291
+ type: string
8292
+ role:
8293
+ type: string
8294
+ enum: [admin, editor, viewer]
8295
+ status:
8296
+ $ref: "#/components/schemas/WorkspaceInvitationStatusDto"
8297
+ maskedEmail:
8298
+ type: string
8299
+ example: j••••e@example.com
8300
+ invitedByName:
8301
+ type: string
8302
+ nullable: true
8303
+ expiresAt:
8304
+ type: string
8305
+ format: date-time
8306
+
8307
+ WorkspaceInvitationListDto:
8308
+ type: object
8309
+ required: [invitations]
8310
+ properties:
8311
+ invitations:
8312
+ type: array
8313
+ items:
8314
+ $ref: "#/components/schemas/WorkspaceInvitationDto"
8315
+
8316
+ InviteTeamMemberResultDto:
8317
+ type: object
8318
+ description: >
8319
+ Discriminated on `outcome`. `invited` carries the new pending invitation;
8320
+ `already_member` carries the existing membership row unchanged.
8321
+ required: [outcome]
8322
+ properties:
8323
+ outcome:
8324
+ type: string
8325
+ enum: [invited, already_member]
8326
+ invitation:
8327
+ $ref: "#/components/schemas/WorkspaceInvitationDto"
8328
+ member:
8329
+ $ref: "#/components/schemas/TeamMemberDto"
8330
+
8032
8331
  EraseInsightsPersonBodyDto:
8033
8332
  type: object
8034
8333
  description: >
@@ -12982,6 +13281,13 @@ components:
12982
13281
  type: string
12983
13282
  format: date-time
12984
13283
  nullable: true
13284
+ dimensionScores:
13285
+ type: array
13286
+ items:
13287
+ $ref: "#/components/schemas/ReadinessScoreHistoryDimensionScoreDto"
13288
+ description: >
13289
+ Per-dimension percentages from the run's readiness_output_contract when present.
13290
+ Omitted when the run has no stored dimension breakdown.
12985
13291
 
12986
13292
  ReadinessRepoRunListDto:
12987
13293
  type: object
@@ -14637,6 +14943,8 @@ components:
14637
14943
  - integrations.cursor.platform_key_enabled
14638
14944
  - integrations.cursor.platform_key_disabled
14639
14945
  - member.invited
14946
+ - member.invite_accepted
14947
+ - member.invite_rejected
14640
14948
  - member.role_changed
14641
14949
  - member.removed
14642
14950
  - hris.personio.sync_started
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkai/tai-api-contract",
3
- "version": "2.69.0",
3
+ "version": "2.71.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",